Repository: nvaccess/nvda

Issue:
NVDA 2019.3Beta1 crashes when setting synthesizer to eSpeak NG if path name contains accented character
<!--
Please thoroughly read NVDA's wiki article on how to fill in this template, including how to provide the required files.
Issues may be closed if the required information is not present.
https://github.com/nvaccess/nvda/wiki/Github-issue-template-explanation-and-examples
-->

### Steps to reproduce:

1. Start NVDA 2019.3 beta 1.
2. Create a portable copy.
3. When asked where to create the portable copy, specify a folder name which includes a character such as é, ç, ş, ğ.
4. Start the portable version.
5. Open NVDA's synthesizer dialog (NVDA+control+g)
6. Press HOME or use the arrow keys to select the "eSpeak NG" synthesizer.
7. Press ENTER.

### Actual behavior:

At the last step, NVDA immediately stops running (visually, the icon stays in the system tray until you mouse over it, that's simply an artifact of Windows not cleaning up properly - NVDA is not running by this point).

The very last entry in my NVDA log at Debug level is:

IO - inputCore.InputManager.executeGesture (13:03:39.817) - winInputHook (17464):
Input: kb(desktop):enter

(Registering the ENTER key being pressed to activate the OK button, set the synthesizer and close the synthesizer dialog.)


### Expected behavior:

Synthesizer should switch to eSpeak NG and NVDA should continue working normally.

### System configuration
#### NVDA installed/portable/running from source:
Portable, and setup in a folder which includes an accented character such as é, ç, ş, ğ

#### NVDA version:

2019.3 beta 1 (which uses eSpeak NG 1.51-dev, commit ca65812ac6019926f2fbd7f12c92d7edd3701e0c).

#### Windows version:

Windows 10 Version: 
18362.388

#### Name and version of other software in use when reproducing the issue:

#### Other information about your system:

### Other questions
#### Does the issue still occur after restarting your PC?
Yes

#### Have you tried any other versions of NVDA? If so, please report their behaviors.
Yes, 2019.2.1 works fine in this situation.

Issue also filed with eSpeak NG as their issue 692: https://github.com/espeak-ng/espeak-ng/issues/692


Discussion:
Hi, don’t tell me this is a side effect of text handling differences with Python 3 unless this is Espeak NG specific (one way to test is using an alpha release in August with the same setup). Thanks.
Ok I tried Alpha 19128 from early November and it crashed the same way.

I tried the oldest alpha on the snapshots page currently 18581, from Sept 10 and it crashed.

So back to there at least it crashes, and it works back in 2019.2.1
Hi, this suggests it might be a Python 3/path issue, given that those earlier alphas come with older Espeak NG releases. Thanks for clarifying this.

I think this might have to do with us using the wrong encoding for the path.
Does NVDA generate a crash dump?
No, no crash dump.
I can reproduce this bug in NVDA 2019.2.1 also. 
I created a NVDA 2019.3beta1 in a 'tést' folder in my user profile, and NVDA 2019.2.1 in a 'tést2' folder in my user profile. Running both of them, if I switch to espeak NG, NVDA just silently exits.
@Qchristensen  Are you sure your NVDA 2019.2.1 does not crash? Can you try within a 'tést2' directory?
There is a place in eSpeak's code (src/libspeak-ng/espeak_api.c, espeak_initialize) where if the directory cannot be located it literally exits the process. It is probably doing this, rather than actually crashing. Either way, it is still very bad.
I initially created it in a 201921é folder off downloads, and I just tried creating a portable version in c:\tést2 using NVDA 2019.2.1 both times, and I can successfully change to eSpeak NG and use that.  

Ok, in my log when it loads eSpeak it has:

INFO - synthDrivers.espeak.SynthDriver.__init__ (22:30:17.551):
Using eSpeak NG version 1.49.1 dev
DEBUG - speechDictHandler.SpeechDict.load (22:30:17.595):
Loading speech dictionary '.\userConfig\speechDicts\voiceDicts.v1\espeak\espeak-English (Great Britain).dic'...
DEBUG - speechDictHandler.SpeechDict.load (22:30:17.595):
file '.\userConfig\speechDicts\voiceDicts.v1\espeak\espeak-English (Great Britain).dic' not found.
INFO - synthDriverHandler.setSynth (22:30:17.595):
Loaded synthDriver espeak
IO - speech.speak (22:30:17.798):
Speaking [LangChangeCommand ('en_GB'), u'C:\\t\xe9st2  window']

I was about to say if it's using relative paths to find eSpeak it might not notice the base directory, but then the last line is looking in C:\\t\xe9st2 rather than C:\tést2 - is it eSpeak / NVDA or Windows which is converting that name - everywhere I look at it I see C:\tést2

I'm still using Windows 10 Version 1903, build 18362.388 because mine refuses to updated to 1909 (for... reasons)  Not sure if that makes a difference here?
@qchristensen: Could you please test the following try build? Again, 
unpack as portable to a directory with accented characters, and let me 
know if you can switch to eSpeak?

This try build is based on 2019.3beta1, but rather than using 
os.fsencode to encode the path to eSpeak, it uses path.encode('mbcs') 
which I think is more in line with what NVDA did in 2019.2.1 and below.

On my system at least, these two calls seem to return different results.


Also, could you please visit Control Panel -> Region -> Administrative 
tab -> Change system Locale... button:

* What is your Current System Locale combo box set to? mine is English - 
United States.

* Is your Beta: Use unicode utf8 for world wide language support, ticked 
or unticked? Mine is unticked.




For me on Polish Windows 7 x64 both NVDA 2019.2.1 and latest Alpha crashes if placed on a folder with accented characters in its name. The try build created by @michaelDCurran also crashes. What is suspicious however is the fact that @Qchristensen log states:
>  Using eSpeak NG version 1.49.1 dev
 
Whereas my log on NVDA 2019.2.1 contains":

>Using eSpeak NG version 1.49.3 dev
 


On my machine NVDA 2019.1.1 is able to start with eSpeak when placed in a folder containing accented characters, whereas 2019.2 and above are not.
@Qchristensen apologies. I forgot to actually paste the try build link!
Here it is: https://ci.appveyor.com/api/buildjobs/8her0mlyia43ag4w/artifacts/output%2Fnvda_snapshot_try-i10607-19468%2Ca3acf1d5.exe 
Ah I wondered where the link was, I thought I was having a senior moment :)

Ok, setup a portable copy from your test build in c:\tést2 (I deleted the whole directory from my last test there).

NVDA still crashes.  The log looks similar to previous:

IO - speech.speak (13:48:08.588) - MainThread (14020):
Speaking [LangChangeCommand ('en_US'), 'eSpeak NG']
DEBUG - synthDrivers.oneCore.SynthDriver._processQueue (13:48:08.596) - MainThread (14020):
Begin processing speech
IO - inputCore.InputManager.executeGesture (13:48:09.416) - winInputHook (11248):
Input: kb(desktop):enter
DEBUG - synthDrivers.oneCore.SynthDriver.cancel (13:48:09.419) - MainThread (14020):
Cancelling
DEBUG - synthDrivers.oneCore.SynthDriver._callback (13:48:09.422) - Dummy-3 (6284):
Done pushing audio
DEBUG - synthDrivers.oneCore.SynthDriver._processQueue (13:48:09.422) - Dummy-3 (6284):
Calling idle on audio player
DEBUG - synthDrivers.oneCore.SynthDriver._processQueue (13:48:09.423) - Dummy-3 (6284):
Queue empty, done processing
DEBUG - autoSettingsUtils.autoSettings.AutoSettings._saveSpecificSettings (13:48:09.423) - MainThread (14020):
Saved settings for SynthDriver

My system locale is also set to English (United States)
@qchristensen: Can you double check that your NVDA 2019.2.1 is really 
2019.2.1? As another person stated, the eSpeak version in your log is 
wrong. Yours says 1.49.1dev, but 2019.2.1 has 1.49.3dev.

And if this is true, then this crash is a regression introduced in 
eSpeak between 1.49.1dev and 1.49.3dev, and therefore not a regression 
in NVDA after 2019.2.1.



Ok, so a bit embarrassing, but maybe some unintentional investigation:

I was, as @michaelDCurran suspected, not running 2019.2.1, somehow it was 2018.2.1, so that's a bit embarrassing... but as noted, it was definitely crashing, so maybe a regression after all?

Trying 2019.2.1 and it too crashes.

Trying 2019.1.1 as a portable version in folder c:\tést201911\ - and I can switch to eSpeak and it works just fine.

Sorry for the confusion!
I am afraid it is at least partially Python 3 regression after all.
Tested as follows:
1. Created a directory called d:\ąśę\
2. In it created portable copy of nvda_snapshot_alpha-18064,e579eb02 (This is the last Alpha powered by Python 2.)
3. Tried to start it - as I am on Windows 7 eSpeak is the default - it starts correctly.
4. Cleaned the directory, and created a portable copy of nvda_snapshot_alpha-18178,ba6b4f6b (that is first Alpha powered by Python 3).
5. Tried to start it - it crashes during startup.


According to https://github.com/nvaccess/nvda/issues/10607#issuecomment-565915565 by @michaelDCurran this is likely caused by an error in espeak. Given that, and due to the imminent release of 2019.3, we only have two options:
- Revert espeak to a prior version and test if that works. Though note that many users wanted espeak to be updated.
- Accept that NVDA with espeak will crash when run under paths with accented characters.

Hopefully most users (who run from an installed copy) aren't affected by this, others who run from a portable copy should have control over the file path and unfortunately will have to avoid accented characters for now. We won't aim to fix this for 2019.3.
Hi Reef,

Sorry, but espeak project is no more a serious project…

The last version of espeak if you want to ask me was espeak 1.48.15.

When the new team of developers started to develop espeak NG, the quality of the synthesizer started to get worse, even they don’t care about windows, and they are linux minded people. 

We really don’t know, which the next bug will be?

They even started to add the languages with the incomplete language / linguistica data. The example is bashkir.

Chinese in espeak is broken, and so on.

And… reporting bugs to them make no sense.

Investigating further, eSpeak actually has a flag to disable the exit call: espeakINITIALIZE_DONT_EXIT=0x8000 which can be passed to the options argument of espeak_initialize.
Doing this stops NVDA from exiting if espeak fails to initialize. this means that if the user is using another synth, and then tries to switch to espeak, they will get an error and NVDA will continue using the original synth. If eSpeak is configured to start with eSpeak (or is on Win 7 with no configuration) then  it will try espeak which will fail, and then switch to silence. The user could then press control+NVDA+s and choose another synth if available.
I think for 2020.1 this is a suitable enough fix.

Resolving patch:
diff --git a/source/synthDrivers/_espeak.py b/source/synthDrivers/_espeak.py
--- a/source/synthDrivers/_espeak.py
+++ b/source/synthDrivers/_espeak.py
@@ -83,6 +83,9 @@
 #EE_BUFFER_FULL=1
 #EE_NOT_FOUND=2
 
+# eSpeak initialization flags
+espeakINITIALIZE_DONT_EXIT = 0x8000
+
 class espeak_EVENT_id(Union):
 	_fields_=[
 		('number',c_int),
@@ -329,10 +332,14 @@ def initialize(indexCallback=None):
 	espeakDLL.espeak_GetCurrentVoice.restype=POINTER(espeak_VOICE)
 	espeakDLL.espeak_SetVoiceByName.argtypes=(c_char_p,)
 	eSpeakPath=os.path.abspath("synthDrivers")
-	sampleRate=espeakDLL.espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS,300,
-		os.fsencode(eSpeakPath),0)
-	if sampleRate<0:
-		raise OSError("espeak_Initialize %d"%sampleRate)
+	sampleRate = espeakDLL.espeak_Initialize(
+		AUDIO_OUTPUT_SYNCHRONOUS, 300,
+		os.fsencode(eSpeakPath),
+		# #10607: ensure espeak does not exit NVDA's process on errors such as the espeak path being invalid.
+		espeakINITIALIZE_DONT_EXIT
+	)
+	if sampleRate <= 0:
+		raise OSError(f"espeak_Initialize failed with code {sampleRate}. Given Espeak data path of {eSpeakPath}")
 	player = nvwave.WavePlayer(channels=1, samplesPerSec=sampleRate, bitsPerSample=16,
 		outputDevice=config.conf["speech"]["outputDevice"],
 		buffered=True)


Test patch: