Skip to content

Commit

Permalink
Fix SAPI5 Audio Ducking Crash on Slow startup (#13699)
Browse files Browse the repository at this point in the history
Fixes #13694

Summary of the issue:
NVDA start up is slow.
Speech is announced during start up to warn the user that start up is slow.
If using SAPI5 and audioducking is enabled, SAPI5 will try to duck the audio using wx callbacks
The wxWidgets App has not beeing initialized, so the callback fails and NVDA crashes.
Description of how this pull request fixes the issue:
Throw a known exception if NVDA is not ready when callLater is called.
If this exception occurs when audio ducking, force audio ducking to unduck immediately instead of delay

Testing strategy:
Manual testing:

Using NVDA 2022.1rc1 with audio ducking enabled and SAPI5.

emulate a slow start up and reproduce the crash.
Using a try build from this PR with audio ducking enabled and SAPI5.

emulate a slow start up.
Note that the warning message that announces that NVDA start up is slow doesn't duck audio
Note that NVDA starts successfully
Confirm that audio ducking works as expected once NVDA has started
Note the following log
WARNING - mathPres.initialize (12:46:35.192) - MainThread (6344):
MathPlayer 4 not available
DEBUGWARNING - core.main (12:46:40.207) - MainThread (6344):
Slow starting core (6.88 sec)
IO - speech.speech.speak (12:46:40.207) - MainThread (6344):
Speaking [LangChangeCommand ('en_US'), 'Loading NVDA. Please wait...']
DEBUGWARNING - characterProcessing._getSpeechSymbolsForLocale (12:46:40.208) - MainThread (6344):
No CLDR data for locale en_US
DEBUGWARNING - synthDrivers.sapi5.SynthDriver.speak (12:46:40.344) - MainThread (6344):
Unsupported speech command: LangChangeCommand ('en_US')
DEBUGWARNING - audioDucking._unensureDucked (12:46:40.349) - MainThread (6344):
wx App not initialized, cannot delay audio un-duck
INFO - core.main (12:46:40.349) - MainThread (6344):
Using wx version 4.1.1 msw (phoenix) wxWidgets 3.1.5 with six version 1.16.0
  • Loading branch information
seanbudd authored May 18, 2022
1 parent 7b488cc commit 8f8d666
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 3 deletions.
9 changes: 7 additions & 2 deletions source/audioDucking.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,13 @@ def _unensureDucked(delay=True):
import core
if _isDebug():
log.debug("Queuing _unensureDucked")
core.callLater(1000,_unensureDucked,False)
return
try:
core.callLater(1000, _unensureDucked, False)
return
except core.NVDANotInitializedError:
# If the wx.App has not been initialized, audio ducking callbacks
# will fail as they rely on wx.CallLater/wx.CallAfter
log.debugWarning("wx App not initialized, unducking immediately")
with _duckingRefCountLock:
_duckingRefCount-=1
if _isDebug():
Expand Down
11 changes: 10 additions & 1 deletion source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -866,13 +866,22 @@ def requestPump():
import wx
wx.CallAfter(_pump.Start,PUMP_MAX_DELAY, True)


class NVDANotInitializedError(Exception):
pass


def callLater(delay, callable, *args, **kwargs):
"""Call a callable once after the specified number of milliseconds.
As the call is executed within NVDA's core queue, it is possible that execution will take place slightly after the requested time.
This function should never be used to execute code that brings up a modal UI as it will cause NVDA's core to block.
This function can be safely called from any thread.
This function can be safely called from any thread once NVDA has been initialized.
"""
import wx
if wx.GetApp() is None:
# If NVDA has not fully initialized yet, the wxApp may not be initialized.
# wx.CallLater and wx.CallAfter requires the wxApp to be initialized.
raise NVDANotInitializedError("Cannot schedule callable, wx.App is not initialized")
if threading.get_ident() == mainThreadId:
return wx.CallLater(delay, _callLaterExec, callable, args, kwargs)
else:
Expand Down

0 comments on commit 8f8d666

Please sign in to comment.