Skip to content

Conversation

@mooreg-si
Copy link

SAPI Voice Change

Adds the ability to change the voice used by the Windows SAPI API to allow the screen reader language to be changed mid-game. An English language voice, like Microsoft David, would not be able to correctly read Spanish content so a Spanish language voice, like Microsoft Sabina, is required. This is intended for use in public installations where a user cannot configure their preferred language as they would on their own personal device. The language can now be changed in-game and be read correctly by the Windows SAPI API. This only works with the Windows SAPI API.

WindowsTTS.cs

Adds SetVoice(string name) function to WindowsTTS class. This will use the WindowsTTS dll to change the voice used by the Windows SAPI API. Takes the Name attribute of an installed SAPI voice as the parameter. Available names can be found in the registry under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\[voice]\Attributes.
Example
WindowsTTS.SetVoice("Microsoft David Desktop");

WindowsTTS.dll

SetVoiceSAPI function will now validate cpToken before attempting to call ISpVoice::SetVoice(cpToken). This prevents Unity crashes when invalid voice provided. Compiled, updated, dll added to Plugins/x86_x64 directory.

Prevents Unity crashes if no voice found.
HResult to SPFindBestToken unused.
Adds function to change the voice to the WindowsTTS class when using the Windows SAPI API.
Formatting to match upstream. Should clean up diff.
@mikrima
Copy link
Owner

mikrima commented Feb 15, 2022

This is amazing. Could you also create a 32Bit version of the compiled DLL? Otherwise this feature would only be available in the 64Bit. Granted, that's probably 99.99% of all users :D but it could lead to some really confusing bug reports

@mooreg-si
Copy link
Author

This is amazing. Could you also create a 32Bit version of the compiled DLL? Otherwise this feature would only be available in the 64Bit. Granted, that's probably 99.99% of all users :D but it could lead to some really confusing bug reports

No problem. I could actually see us being in that 1% that needs 32bit. I've got another update to enable rate changes that I was just cleaning up so I will combine it all into one pull request.

@mooreg-si
Copy link
Author

SAPI Speech Rate Change

Adds ability to change SAPI speech rate. This is intended for use in public installations where a user cannot configure their preferred rate as they would on their own personal device.

Voice Change Method Added to UAP_AccesibilityManager

Adds SetVoice(string voice) method to UAP_AccessibilityManager

Voice Change added and Speech Rate method updated in UAP_AudioQueue

  • Adds SetVoice(string newVoice) method to UAP_AudioQueue. Will handle call from UAP_Accessibility manager and pass them to WindowsTTS.SetVoice when WindowsTTS is used.
  • Updates SetSpeechRate method to also call WindowsTTS.SetSpeechRate when WindowsTTS is in use.
  • WindowsTTS.SetSpeechRate will scale the 1-100 speech rate value from UAP_AudioQueue.SetSpeechRate to the -10-10 value expected by the Windows SAPI API.

Adds Win32 dll to UAP/Plugins/x86

Fixes WindowsTTS Crash

Allows SetVoice and SetSpeechRate to be called from UAP_Accessibility manager, instead of directly through WindowsTTS.cs. This prevents an edge case where Unity would crash if WindowsTTS.SetVoice or WindowsTTS.SetSpeechRate was called without UAP_AccessibilityManager enabled.

@DDRKirbyISQ
Copy link

Just a note for anyone finding this -- I believe the change still works as advertised (I have to debug an issue on my end where calling SetVoice too early / at the wrong time causes a Unity crash, but hopefully that's minor / on me), but one thing I noticed is that the current solution lacks Unicode support, which causes a string such as 你好 to be interpreted as 你好, which of course is useless if fed to SAPI.

I'm not great with native plugins and c++ but from my eyes it seems like this is actually a pretty easy fix -- instead of CP_ACP we should use CP_UTF8 in AddToSpeechQueue:

// old
// ::MultiByteToWideChar(CP_ACP, NULL, text, -1, wText, len);

// new
::MultiByteToWideChar(CP_UTF8, NULL, text, -1, wText, len);

(see https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar)

I was able to recompile the DLL myself and to a quick test to verify that this seems to get unicode support working ok. If I have some time later this week I might upload that to another fork just so anyone coming here in the future has it on hand.

For what it's worth, it is also possible to get different speakers in the MacOS TTS implementation by providing an additional "-v [voicename]" additional argument to the "say" invocation.

DDRKirbyISQ added a commit to DDRKirbyISQ/UnityAccessibilityPlugin that referenced this pull request Sep 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants