Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speech synthesizer locale is overwritten by route locale at every spoken instruction point #3799

Open
1ec5 opened this issue Mar 24, 2022 · 4 comments
Labels
bug Something isn’t working jira-sync-complete platform parity Required to keep on par with Android. topic: voice

Comments

@1ec5
Copy link
Contributor

1ec5 commented Mar 24, 2022

The speech synthesizer’s locale is overwritten by the route’s locale at every spoken instruction point, preventing the application from hard-coding a particular locale for selecting the text-to-speech voice.

Background

Normally, the navigation SDK is designed to speak spoken instructions in the same locale that is indicated in the route data. After all, if the guidance instructions are phrased as appropriate for French, they should be spoken by a French voice, not an American English one. 🙉 We want to do the right thing by default, without the developer needing to remember to set both locale settings.

However, the application should have the opportunity to hard-code the voice locale, for example, Hong Kong English instructions spoken by a British English voice. This is normally quite a contrived scenario, but a practical need for this mismatch can arise due to missing language support in either the Directions API or the Voice API.

Problem

Currently, developers get the impression that they can simply set SpeechSynthesizing.locale to the desired voice locale, such as in the process of configurating NavigationOptions. Unfortunately, RouteVoiceController.didPassSpokenInstructionPoint(notification:) overwrites this locale with the requested Directions API locale immediately before prefetching or saying an instruction. The prefetching and speaking is based on the resolved locale in the route response.

speechSynthesizer.locale = routeProgress.routeOptions.locale
let locale = routeProgress.route.speechLocale

This issue even affects any custom speech synthesizer that the application provides per #3747.

Proposed solution

We should move this locale-setting code so that it executes less frequently, like once at the beginning of the route, since we don’t support switching languages in the middle of a route anyways. We should only set overwrite the locale if it’s nil or equivalent to Locale.autoupdatingCurrent.

/ref #1649 #2348
/cc @mapbox/navigation-ios @Guardiola31337 @browndp08 @jinny-nam @dgearhart (FYI)

@1ec5 1ec5 added bug Something isn’t working topic: voice labels Mar 24, 2022
@1ec5
Copy link
Contributor Author

1ec5 commented Mar 24, 2022

Ideally, the Directions API and Voice API would fully support the locale that the application wants to use. In the unfortunate event that some language support is missing, a partial client-side workaround may be possible as a last resort.

For an application based on a fork of the navigation SDK, the simplest workaround would be to remove the offending lines or hard-code them to use the desired locale.

If forking is not an option, an alternative workaround would be to shadow the voice locale when making requests to the Voice API:

import MapboxSpeech

/// Speaks in the Received Pronunciation of Commonwealth English regardless of what was requested.
class CommonwealthSpeechSynthesizer: SpeechSynthesizer {
    override func audioData(with options: SpeechOptions, completionHandler: @escaping SpeechSynthesizer.CompletionHandler) -> URLSessionDataTask {
        options.locale = Locale(identifier: "en-GB")
        return super.audioData(with: options, completionHandler: completionHandler)
    }
}

let commonwealthSpeechSynthesizer = CommonwealthSpeechSynthesizer(accessToken: nil)
let mapboxSpeechSynthesizer = MapboxSpeechSynthesizer(remoteSpeechSynthesizer: commonwealthSpeechSynthesizer)
let muxedSpeechSynthesizer = MultiplexedSpeechSynthesizer([mapboxSpeechSynthesizer, SystemSpeechSynthesizer()], accessToken: nil, host: nil)
let routeVoiceController = RouteVoiceController(navigationService: navigationService, speechSynthesizer: muxedSpeechSynthesizer, accessToken: nil, host: nil)
let navigationOptions = NavigationOptions(navigationService: navigationService, voiceController: routeVoiceController, )

This workaround requires the changes in #3747. It’s especially involved because MapboxSpeech doesn’t expose a hook for custom URL query parameters: mapbox/mapbox-speech-swift#50. It also would not cover offline speech by VoiceOver, because SystemSpeechSynthesizer.locale is merely public, not open. (That might be worth addressing at the same time.)

@1ec5 1ec5 added the platform parity Required to keep on par with Android. label Mar 24, 2022
@mcrollin
Copy link

I too am experiencing the issue. Mid-navigation, the instructions switch from German to English.

@1ec5
Copy link
Contributor Author

1ec5 commented Apr 7, 2022

To be clear, the issue described here is only about the voice (i.e., the accent), not the instruction (the wording).

@mcrollin
Copy link

mcrollin commented Apr 7, 2022

Well, we have users experiencing random language changes from an instruction to another (ie: German to English and back). I thought that it might be linked to this issue. If it isn't I'm happy to file new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn’t working jira-sync-complete platform parity Required to keep on par with Android. topic: voice
Projects
None yet
Development

No branches or pull requests

3 participants