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
Support for Windows OneCore voices included in Windows 10. #7110
Merged
Merged
Changes from 7 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
d338e47
Support for Windows OneCore voices included in Windows 10.
jcsteh ddec1b4
Fix broken CharacterModeCommand(False) for SSML. Test this and Phonem…
jcsteh 5973c2e
speechXml: Escape attribute values.
jcsteh 3632020
Review actions.
jcsteh 3b43394
Don't break subsequent output if a speech call fails asynchronously.
jcsteh c02a788
speechXml: Don't ever output invalid XML characters.
jcsteh 1cf30e9
Review actions.
jcsteh ba62552
Bundle the VC 2015 runtime, as some systems don't have it and it is n…
jcsteh f1a34ce
OneCore synth driver: Ignore invalid languages/languages unknown to W…
jcsteh 1f9f90a
Add User Guide section for Windows OneCore Voices.
jcsteh 032e65e
OneCore synth driver: Exclude from available synths if earlier than W…
jcsteh 1cb911f
Review actions.
jcsteh d07bc1d
readme: Update Visual Studio dependencies to note the new dependency …
jcsteh File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,3 +54,4 @@ EXPORTS | |
dllImportTableHooks_hookSingle | ||
dllImportTableHooks_unhookSingle | ||
audioDucking_shouldDelay | ||
logMessage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* | ||
Code for C dll bridge to Windows OneCore voices. | ||
This file is a part of the NVDA project. | ||
URL: http://www.nvaccess.org/ | ||
Copyright 2016-2017 Tyler Spivey, NV Access Limited. | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License version 2.0, as published by | ||
the Free Software Foundation. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
This license can be found at: | ||
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
*/ | ||
|
||
#include <string> | ||
#include <collection.h> | ||
#include <ppltasks.h> | ||
#include <wrl.h> | ||
#include <robuffer.h> | ||
#include <common/log.h> | ||
#include "oneCoreSpeech.h" | ||
|
||
using namespace std; | ||
using namespace Platform; | ||
using namespace Windows::Media::SpeechSynthesis; | ||
using namespace concurrency; | ||
using namespace Windows::Storage::Streams; | ||
using namespace Microsoft::WRL; | ||
using namespace Windows::Media; | ||
using namespace Windows::Foundation::Collections; | ||
|
||
byte* getBytes(IBuffer^ buffer) { | ||
// We want direct access to the buffer rather than copying it. | ||
// To do this, we need to get to the IBufferByteAccess interface. | ||
// See http://cm-bloggers.blogspot.com/2012/09/accessing-image-pixel-data-in-ccx.html | ||
ComPtr<IInspectable> insp = reinterpret_cast<IInspectable*>(buffer); | ||
ComPtr<IBufferByteAccess> bufferByteAccess; | ||
if (FAILED(insp.As(&bufferByteAccess))) { | ||
LOG_ERROR(L"Couldn't get IBufferByteAccess from IBuffer"); | ||
return nullptr; | ||
} | ||
byte* bytes = nullptr; | ||
bufferByteAccess->Buffer(&bytes); | ||
return bytes; | ||
} | ||
|
||
OcSpeech* __stdcall ocSpeech_initialize() { | ||
auto instance = new OcSpeech; | ||
instance->synth = ref new SpeechSynthesizer(); | ||
return instance; | ||
} | ||
|
||
void __stdcall ocSpeech_terminate(OcSpeech* instance) { | ||
delete instance; | ||
} | ||
|
||
void __stdcall ocSpeech_setCallback(OcSpeech* instance, ocSpeech_Callback fn) { | ||
instance->callback = fn; | ||
} | ||
|
||
void __stdcall ocSpeech_speak(OcSpeech* instance, char16 *text) { | ||
String^ textStr = ref new String(text); | ||
auto markersStr = make_shared<wstring>(); | ||
task<SpeechSynthesisStream ^> speakTask; | ||
try { | ||
speakTask = create_task(instance->synth->SynthesizeSsmlToStreamAsync(textStr)); | ||
} catch (Platform::Exception ^e) { | ||
LOG_ERROR(L"Error " << e->HResult << L": " << e->Message->Data()); | ||
instance->callback(NULL, 0, NULL); | ||
return; | ||
} | ||
speakTask.then([markersStr] (SpeechSynthesisStream^ speechStream) { | ||
// speechStream->Size is 64 bit, but Buffer can only take 32 bit. | ||
// We shouldn't get values above 32 bit in reality. | ||
const unsigned int size = static_cast<unsigned int>(speechStream->Size); | ||
Buffer^ buffer = ref new Buffer(size); | ||
IVectorView<IMediaMarker^>^ markers = speechStream->Markers; | ||
for (auto&& marker : markers) { | ||
if (markersStr->length() > 0) { | ||
*markersStr += L"|"; | ||
} | ||
*markersStr += marker->Text->Data(); | ||
*markersStr += L":"; | ||
*markersStr += to_wstring(marker->Time.Duration); | ||
} | ||
auto t = create_task(speechStream->ReadAsync(buffer, size, Windows::Storage::Streams::InputStreamOptions::None)); | ||
return t; | ||
}).then([instance, markersStr] (IBuffer^ buffer) { | ||
// Data has been read from the speech stream. | ||
// Pass it to the callback. | ||
byte* bytes = getBytes(buffer); | ||
instance->callback(bytes, buffer->Length, markersStr->c_str()); | ||
}).then([instance] (task<void> previous) { | ||
// Catch any unhandled exceptions that occurred during these tasks. | ||
try { | ||
previous.get(); | ||
} catch (Platform::Exception^ e) { | ||
LOG_ERROR(L"Error " << e->HResult << L": " << e->Message->Data()); | ||
instance->callback(NULL, 0, NULL); | ||
} | ||
}); | ||
} | ||
|
||
// We use BSTR because we need the string to stay around until the caller is done with it | ||
// but the caller then needs to free it. | ||
// We can't just use malloc because the caller might be using a different CRT | ||
// and calling malloc and free from different CRTs isn't safe. | ||
BSTR __stdcall ocSpeech_getVoices(OcSpeech* instance) { | ||
wstring voices; | ||
for (unsigned int i = 0; i < instance->synth->AllVoices->Size; ++i) { | ||
VoiceInformation^ info = instance->synth->AllVoices->GetAt(i); | ||
voices += info->Id->Data(); | ||
voices += L":"; | ||
voices += info->DisplayName->Data(); | ||
if (i != instance->synth->AllVoices->Size - 1) { | ||
voices += L"|"; | ||
} | ||
} | ||
return SysAllocString(voices.c_str()); | ||
} | ||
|
||
const char16* __stdcall ocSpeech_getCurrentVoiceId(OcSpeech* instance) { | ||
return instance->synth->Voice->Id->Data(); | ||
} | ||
|
||
void __stdcall ocSpeech_setVoice(OcSpeech* instance, int index) { | ||
instance->synth->Voice = instance->synth->AllVoices->GetAt(index); | ||
} | ||
|
||
const char16 * __stdcall ocSpeech_getCurrentVoiceLanguage(OcSpeech* instance) { | ||
return instance->synth->Voice->Language->Data(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
Header for C dll bridge to Windows OneCore voices. | ||
This file is a part of the NVDA project. | ||
URL: http://www.nvaccess.org/ | ||
Copyright 2016-2017 Tyler Spivey, NV Access Limited. | ||
This program is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License version 2.0, as published by | ||
the Free Software Foundation. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
This license can be found at: | ||
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
*/ | ||
|
||
#pragma once | ||
#define export __declspec(dllexport) | ||
|
||
typedef void (*ocSpeech_Callback)(byte* data, int length, const char16* markers); | ||
typedef struct { | ||
Windows::Media::SpeechSynthesis::SpeechSynthesizer ^synth; | ||
ocSpeech_Callback callback; | ||
} OcSpeech; | ||
|
||
extern "C" { | ||
export OcSpeech* __stdcall ocSpeech_initialize(); | ||
export void __stdcall ocSpeech_terminate(OcSpeech* instance); | ||
export void __stdcall ocSpeech_setCallback(OcSpeech* instance, ocSpeech_Callback fn); | ||
export void __stdcall ocSpeech_speak(OcSpeech* instance, char16 *text); | ||
export BSTR __stdcall ocSpeech_getVoices(OcSpeech* instance); | ||
export const char16* __stdcall ocSpeech_getCurrentVoiceId(OcSpeech* instance); | ||
export void __stdcall ocSpeech_setVoice(OcSpeech* instance, int index); | ||
export const char16* __stdcall ocSpeech_getCurrentVoiceLanguage(OcSpeech* instance); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
### | ||
#This file is a part of the NVDA project. | ||
#URL: http://www.nvaccess.org/ | ||
#Copyright 2016-2017 NV Access Limited | ||
#This program is free software: you can redistribute it and/or modify | ||
#it under the terms of the GNU General Public License version 2.0, as published by | ||
#the Free Software Foundation. | ||
#This program is distributed in the hope that it will be useful, | ||
#but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
#This license can be found at: | ||
#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | ||
### | ||
|
||
Import( | ||
'env', | ||
'sourceDir', | ||
'libInstallDir', | ||
'localLib', | ||
) | ||
|
||
TARGET_ARCH=env['TARGET_ARCH'] | ||
debug=env['nvdaHelperDebugFlags'] | ||
release=env['release'] | ||
signExec=env['signExec'] if env['certFile'] else None | ||
|
||
env.Append(CPPDEFINES=[ | ||
'UNICODE', '_CRT_SECURE_NO_DEPRECATE', | ||
('LOGLEVEL','${nvdaHelperLogLevel}')]) | ||
env.Append(CCFLAGS=['/W3', '/WX']) | ||
env.Append(CXXFLAGS=['/EHsc', '/ZW', | ||
r'/AIC:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcpackages', | ||
r'/AIC:\Program Files (x86)\Windows Kits\10\UnionMetadata']) | ||
env.Append(CPPPATH=[Dir('..').abspath]) | ||
env.Append(LINKFLAGS=['/incremental:no', '/WX']) | ||
env.Append(LINKFLAGS='/release') # We always want a checksum in the header | ||
|
||
if not release: | ||
env.Append(CCFLAGS=['/Od']) | ||
else: | ||
env.Append(CCFLAGS='/O2') | ||
env.Append(CCFLAGS='/GL') | ||
env.Append(LINKFLAGS=['/LTCG']) | ||
|
||
if 'RTC' in debug: | ||
env.Append(CCFLAGS=['/RTCsu']) | ||
|
||
# We always want debug symbols | ||
env.Append(PDB='${TARGET}.pdb') | ||
env.Append(LINKFLAGS='/OPT:REF') #having symbols usually turns this off but we have no need for unused symbols | ||
|
||
localWin10Lib = env.SharedLibrary( | ||
target="nvdaHelperLocalWin10", | ||
source=[ | ||
env['projectResFile'], | ||
'oneCoreSpeech.cpp', | ||
], | ||
LIBS=["oleaut32", localLib[2]], | ||
) | ||
if signExec: | ||
env.AddPostAction(localWin10Lib[0], [signExec]) | ||
env.Install(libInstallDir, localWin10Lib) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can inst.as fail? I.e. that interface is not supported? and perhaps then the pointer would be NULL?