尝试在Delphi 10.3中使用带有日语引擎(MS Haruka(的MS Speech API v11。
我有一个带有表单和按钮的示例应用程序。点击处理程序:
uses SpeechLib11_TLB; // Imported from "Microsoft Speech Object Library" v.B.0
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpeechVoice;
begin
v := CoSpVoice.Create();
//5.4 only but won't hurt
v.Voice := v.GetVoices('language=411', '').Item(0);
v.Speak('時間', SVSFDefault);
end;
这会导致一个错误,即"灾难性故障"(HRESULT 0x8000FFFF,E_UNEXPECTED(。我认为应该在C++项目中等效的代码:
#include <windows.h>
#import "libid:d3c4a7f2-7d27-4332-b41f-593d71e16db1" rename_namespace("SAPI") //v11
//#import "libid:C866CA3A-32F7-11D2-9602-00C04F8EE628" rename_namespace("SAPI") //v5.4
int wmain()
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
{
SAPI::ISpeechVoicePtr v;
v.CreateInstance(__uuidof(SAPI::SpVoice));
//Needed for 5.4 only, but won't hurt
SAPI::ISpeechObjectTokensPtr voices(v->GetVoices(L"language=411", L""));
v->Voice = voices->Item(0);
v->Speak(L"時間", SAPI::SVSFDefault);
}
CoUninitialize();
return 0;
}
这是有效的,也是有效的。因此SAPI本身在机器上没有损坏。两个项目中的平台都是Win32,而不是Win64。日语语音是默认语音(无需明确设置(。
与SAPI 5.4正确的结果相同(不是OneCore(,尽管日语语音不是默认语音,我不得不添加几行来将其设置为默认语音。
进一步的调试表明,在Delphi端,在第一行之后立即调用v.Voice
属性getter会导致相同的错误E_UNEXPECTED。同时,如果您从GetVoices()
向Voice
setter传递一个有效的语音令牌对象,它就会工作。看起来语音对象在C++中正确地将自己初始化为默认值,但在Delphi项目中不知何故跳过了这一点。
使用SAPI 5.4在Delphi中进行施工后立即请求v.Voice
。调用Speak()
仍然抛出E_UNEXPECTED。
C++和Delphi在进程/线程范围执行上下文方面有什么区别这不是线程区域设置。COM线程模型是两者的单元。
同样的Delphi代码适用于英语短语和英语语音(海伦女士(。因此,无论发生什么初始化失败,都可能是Haruka特有的。
SAPI11运行时在这里可用。TTS的语言数据在这里。
另一个数据点。我在Delphi中重写了SAPI逻辑,改为使用SAPI 5.4 OneCore(而不是SAPI 5.4本身(。与5.4和11不同,它没有公开基于IDispatch的接口,而且它在Delphi中有点笨拙,但日语TTS有效。这个问题,最初提出的,仍然没有答案,但至少有一个解决办法。我会写一个答案,但我不会接受
然而,这并不是自定义与双重区别的原因。我已经更改了逻辑,使用自定义接口而不是自动化接口,SAPI 5.4正确(typelib定义了两者(,仍然从Speak()
获得E_UNEXPECTED
。没有错误信息。
这是另一个漂亮的数据点:SAPI 5.4 TTS与基于自动化的API在Delphi控制台应用程序中按预期工作和对话。所以它甚至不是Delphi特有的,它在某种程度上是VCL特有的。Delphi GUI是什么?不用说,我立即用一个窗体和一个按钮在C++GUI应用程序中重新测试了C++代码段。C++one会说话。
不是一个答案,而是一个解决方法。
Windows10提供了两种风格的32位SAPI。有SAPI 5.4固有(在system32speech
中(,也有SAPI 5.4OneCore(在system32speech_onecore
中(。后者,尽管表面上是一样的,但暴露了一个不同的typelib——没有自动化支持,所有接口都是自定义的,而不是双重的。更重要的是,当你在Windows 10设置应用程序中下载日语TTS语音时,你最终会在OneCore下有3个语音(Sayaka,不知怎么的,缺失了(,只有一个,Haruka,在5.4以下。
Delphi可以在typelib中使用自定义接口,但方法看起来有些笨拙。此外,自动化API中的语音列举更为清晰。不管怎样,开始吧。
uses SpeechLib54Core_TLB; // This time it's OneCore
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpVoice;
cat: ISpObjectTokenCategory;
toks: IEnumSpObjectTokens;
tok: ISpObjectToken;
sno: LongWord;
hr: HResult;
n: Cardinal;
begin
v := CoSpVoice.Create();
hr := v.GetVoice(tok);
tok.GetCategory(cat);
cat.EnumTokens('language=411', '', toks); //411 means Japanese
toks.GetCount(n);
if n = 0 then
exit; // No Japanese voices installed
toks.Item(0, tok); //Take the first one - typically Ayumi
v.SetVoice(tok);
v.Speak('時間', 0, sno);
end;
请注意,将日语字符串文本传递到COM方法时,不需要显式转换为宽字符串。