Skip to content

Support for 32 bit sapi4 and sapi5 via a 32 bit synthDriver runtime #19412

Closed
michaelDCurran wants to merge 41 commits into
nvaccess:betafrom
michaelDCurran:bridge
Closed

Support for 32 bit sapi4 and sapi5 via a 32 bit synthDriver runtime #19412
michaelDCurran wants to merge 41 commits into
nvaccess:betafrom
michaelDCurran:bridge

Conversation

@michaelDCurran

Copy link
Copy Markdown
Member

Link to issue number:

Summary of the issue:

64 bit NVDA can no longer support SAPI4.

Description of user facing changes:

SAPI4 is again availble on 64 bit NVDA.

Description of developer facing changes:

TBD

Description of development approach:

TBD

Testing strategy:

TBD

Known issues with pull request:

None known so far.

Code Review Checklist:

  • [] Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

michaelDCurran and others added 30 commits December 10, 2025 01:10
…ather than 'exposed_' prefix which allows better type checking between proxies and services. Split SynthDriverService.registerNotification into registerSynthIndexReachedNotification and registerSynthDoneSpeakingNotification.
…oved. It will still have read access to the user's files, and write access to the low integrity temp directory.
…rHost32.synthDriver.SynthDriverProxy32. Store actual 32 bit synth drivers in source/synthDrivers32, rather thanb eing built into the runtime. Remove sapi5 32 for now as it is too unstable.
…les / packages for at least sapi4. These need to be explicit as py2exe will no longer scan sapi4.py when building as it is independent of the runtime.
…lled from an elevated process. Use 'removeElevation=True' on SecurePopen to do this.
…er than a strange subset being true. Easier to understand that by default its unrestricted.
… a limited token available). Elevation type of default (which has no linked token at all) is seen when running as system or as local service, so this should be treated as not elevated. Also rewrite how the DACL and SACL are made for the temp window station and desktop, ensuring that even without a restricted token, the SACL is set ensuring that it will work with a low integrity level.
…ng from source.

Use maximum sandboxing when launching the host process.
SynthDriverProxy32's check method now checks if the runtime is actually available.
… username, domain, password and logonType arguments, allowing for much more general logon scenarios.
…uiredRestrictedSids and include them even if they don't appear in existing groups. This doesn't give more access than the original token allows as the original groups still win. But it is to allow system, when its user SID is removed, to still access desktops via the interactive group etc. Also add an includedSidStrings keyword argument so that more SIDs to restrict can be provided E.g. the unique sandbox SID for this token. SecurePopen: always produce a custom unique sandbox SID rather than relying on the Logon SID, which may not always be available (e.g. running as system). Rather than trying to build our own default DACL from scratch, just add the unique sandbox SID to the token's existing default DACL. All these changes allow running as system with a restricted token that does not include system. Though on some machines COM is still limited it seems.
…ew token's default DACL so that the parent has access to any objects created using the new token.

When setting the default DACL and setting the integrity level, ensure not to touch the parent token.
…pen classes to allow configuring of CREATIONFLAGS_CREATE_NO_WINDOW. Set to false by default.
…pen, including logging on by username, create no window, and now the ability to configure if redirection of std handles occurs.
…n dictionaries (one standard and one for secure desktops) at module level so that they can be configured when testing, via the NVDA Pythonconsole.
…command and args to be executed as a Python script using the current Python interpreter as the child process. This makes debugging tests a little easier as then you can guarantee that the child process is the real true Python process and not just another intermediate Py launcher.
…cure desktops runs the runtime inside an appContainer running as local service.
@hwf1324

hwf1324 commented Jan 2, 2026

Copy link
Copy Markdown
Contributor

eSpeak build failed:

Compiling phoneme data
scons: *** [include\espeak\espeak-ng-data\intonations] Could not find module 'D:\NVDA\hwf1324\nvda\build\x86_64\espeak\espeak.dll' (or one of its dependencies). Try using the full path with constructor syntax.
scons: building terminated because of errors.

Also merge the latest beta branch

@michaelDCurran michaelDCurran changed the title Support for SAPI4 via a 32 bit shim runtime Support for 32 bit sapi4 and sapi5 via a 32 bit synthDriver runtime Jan 8, 2026
@michaelDCurran

Copy link
Copy Markdown
Member Author

Closing in favor of a smaller pr that only contains the 32 bit bridge, and none of the security features which will be used by the future ART. These will be added in subsequent releases over 2026.

SaschaCowley pushed a commit that referenced this pull request Jan 27, 2026
…19432)

Replaces pr #19412

### Summary of the issue:
With the switch to 64 bit, NVDA no longer supports sapi4, or 32 bit
specific sapi5 synthesizers. NVDA should somehow continue to support
these synthesizers.

### Description of user facing changes:
NvDA continues to support sapi4 and 32 bit specific Sapi5 synthesizers.

### Description of developer facing changes:

### Description of development approach:
* Compile nvdaHelperLocal for all archetectures, rather than just for
NVDA's core archetecture (x64). NVDA's 32 bit synthDriver host runtime
needs nvdaHelperLocal for WASAPI.
* Move the wasapi function definitions for nvdaHelperLocal.dll into
their own 'wasapi' Python module, so that they can be imported separate
to NvDAHelper. NVDA's 32 bit synthDriver host runtime uses WASAPI
directly.
* Compile sonic independently of eSpeak and for all archetectures (not
just x64), as it is needed by sapi5 when running in NVDA's 32 bit
synthDriver host runtime. The x86 sonic dll will be placed in
source/synthDrivers32.
* winBindings.kernel32: add definition for GetCurrentProcessId. Used in
logging for NVDA's 32 bit synthDriver host runtime.
* Add a jobObject module, which contains a Job class which wraps a win32
Job object, which allows automatically killing any process associated
with it. This is sued to ensure that any process started for the 32 bit
synthDriver host runtime is killed off if NvDA exits.
* Add a _bridge package, which provides a client and runtime for the 32
bit synthDriver host. The client is code that runs in NVDA, and the
runtime is code that runs in its own 32 bit child process. Communication
between NvDA and the child process is via RPYC over the child processs's
standard pipe handles. This _bridge package can be later extended with
more clients and runtimes as we start to implement a full ART.
 
* Add 32 bit sapi4 and sapi5 synthDrivers which use the
synthDriverHost32 runtime.

### Testing strategy:
Manually tested running NvDA with both 32 bit sapi4 (Truevoice and MS
Mike Mary Sam) and 32 bit sapi5 (Windows built-in David, eSpeak bridge,
Mikropuhe). Ran with NvDA launcher, installed normal user, and on secure
desktop (UAC and logon screen).

### Known issues with pull request:
* This PR only supports sapi4 and 32 bit sapi5 quite specifically. It is
not a generalized 32 bit shim for add-ons. To do this, we would need to
include the security features from pr #19412 and greatly increase the
size of the runtime, and add many more proxies and services. In other
words, complete the secure ART project ;). To have something ready for
2026.1, we have decided to limit this to sapi4 and 32 bit sapi5.
SaschaCowley pushed a commit that referenced this pull request Feb 23, 2026
…playing directly. (#19577)

### Summary of the issue:
the 32 bit sapi synthDrivers added in pr #19432 currently play audio
directly. Although this provides good performance and works in most
scenarios, it does not allow supporting audio ducking (as the host
process does not have permission to duck audio, and if NvDA did
ititself, it would inappropriately duck the wrong thing). Also, as we
start moving to a secure add-on runtime, 3rd party synthDrivers (at
least on secure desktops) will most likely be run in an appContainer,
which unfortunately deos not allow the host process to access tha audio
device directly. thus, to work around both of these issues, the 32 bit
sapi synthDrivers should send the audio to NvDA for playback.

### Description of development approach:
Take more code from pr #19412, specifically the WavePlayer service and
proxy, the pipe creation code, and raiiUtils.
NvDA exposes a WavePlayer service, allowing an external process such as
the 32 bit sapi synthDriver processes to send audio.
 
### Testing strategy:
General smoke testing of sapi4 and 32 bit sapi5 synthDrivers.

### Known issues with pull request:
* the Mikropuhe sapi5 voices sound a bit scratchy / jittery like there
are buffer underruns. I may require some assistance in improving the
stability / performance here. Sapi4 truevoice seems to work fine, sapi5
espeak and in-built windows sapi5 voices also seem to be fine.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants