Delphi SAPI 文本转语音

发布于 2024-09-05 02:49:54 字数 1701 浏览 11 评论 0原文

首先:这不是 Delphi 和 SAPI 的重复。我对“SAPI in Delphi”主题有一个具体问题。

我使用了 Delphi 2009 中出色的导入类型库指南来在组件选项板中获取 TspVoice 组件。这很好用。我

var
  SpVoice: TSpVoice;

可以编写

SpVoice.Speak('This is an example.', 1);

以获得异步音频输出。

第一个问题

根据文档,我可以编写

SpVoice.Speak('This is an example.', 0);

以获得同步音频输出,但我得到了 EZeroDivide 异常。这是为什么呢?

第二个问题

但更重要的是,我希望能够动态创建 SpVoice 对象(我认为这被称为“后期绑定”SpVoice 对象),部分因为我的应用程序的所有会话中只有一小部分会使用它,部分原因是我不想假设最终用户系统上存在 SAPI 服务器。

为此,我尝试了,

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: Variant;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SpVoice.Speak('this is a test', 0);
end;

但显然没有任何作用! (用 1 替换 0 会导致 EZeroDivide 异常。)

免责声明

我对 COM/OLE 自动化相当陌生。对于我在这篇文章中表现出的任何无知或愚蠢,我深表歉意...

更新

为了每个遇到与我相同问题的人的利益,François 的视频解释了 SAPI/Windows 中存在一个错误(某些地方不兼容),这使得以下代码引发 EZeroDivide 异常:

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: variant;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SpVoice.Speak('This is a text.');
end;

如视频所示,解决方案是更改 FPU 控制字:

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: variant;
  SavedCW: Word;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SavedCW := Get8087CW;
  Set8087CW(SavedCW or $4);
  SpVoice.Speak('This is a text.');
  Set8087CW(SavedCW);
end;

另外,如果您想异步播放声音,则必须确保播放器不超出范围!

First of all: this is not a duplicate of Delphi and SAPI. I have a specific problem with the "SAPI in Delphi" subject.

I have used the excellent Import Type-Library guide in Delphi 2009 to get a TSpVoice component in the component palette. This works great. With

var
  SpVoice: TSpVoice;

I can write

SpVoice.Speak('This is an example.', 1);

to get asynchronous audio output.

First question

According to the documentation, I would be able to write

SpVoice.Speak('This is an example.', 0);

to get synchronous audio output, but instead I get an EZeroDivide exception. Why's that?

Second question

But more importantly, I would like to be able to create the SpVoice object dynamically (I think this is called to "late-bind" the SpVoice object), partly because only a very small fraction of all sessions of my app will use it, and partly because I do not want to assume the existance of the SAPI server on the end-user's system.

To this end, I tried

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: Variant;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SpVoice.Speak('this is a test', 0);
end;

which apparently does nothing at all! (Replacing the 0 with 1 gives me the EZeroDivide exception.)

Disclaimer

I am rather new to COM/OLE automation. I am sorry for any ignorance or stupidity shown by me in this post...

Update

For the benefit of everyone encountering the same problem as I did, the video by François explained there is a bug in SAPI/Windows (some incompatibility somewhere), which makes the following code raise the EZeroDivide exception:

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: variant;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SpVoice.Speak('This is a text.');
end;

The solution, as presented by the video, is to alter the FPU control word:

procedure TForm1.FormClick(Sender: TObject);
var
  SpVoice: variant;
  SavedCW: Word;
begin
  SpVoice := CreateOleObject('SAPI.SpVoice');
  SavedCW := Get8087CW;
  Set8087CW(SavedCW or $4);
  SpVoice.Speak('This is a text.');
  Set8087CW(SavedCW);
end;

And, in addition, if you want to play a sound asynchronously, then you have to make sure that the player doesn't go out of scope!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

后eg是否自 2024-09-12 02:49:54

您可能会对 此 CodeRage 4 会议感兴趣,时间为 "支持语音的 Delphi 应用程序 (zip)"
您将获得您正在寻找的“操作方法”...
(我猜你使用的是 Vista 或 +,因为 XP 上没有发生零除法)

You may find interesting to see this CodeRage 4 session on "Speech Enabling Delphi Applications (zip)"
You'll get the "how-to" you're looking for...
(and I guess you are on Vista or + as the the zero divide did not happend on XP)

糖果控 2024-09-12 02:49:54

我在 Delphi XE2 中遇到了同样的问题。问题中提出的 Set8087CW(SavedCW or $4) 解决方案对我不起作用。它只是用另一个浮点异常替换了除零异常。

对我有用的是:

SavedCW := Get8087CW;
SetFPUExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
SpVoice.Speak('All floating point exceptions disabled!', 0);
Set8087CW(SavedCW);

I was having the same problem in Delphi XE2. The Set8087CW(SavedCW or $4) solution presented in the question did not work for me. It merely replaced the division by zero exception with another floating point exception.

What did work for me is this:

SavedCW := Get8087CW;
SetFPUExceptionMask([exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
SpVoice.Speak('All floating point exceptions disabled!', 0);
Set8087CW(SavedCW);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文