Delphi SAPI Text-To-Speech

12,246

Solution 1

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)

Solution 2

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);
Share:
12,246
Andreas Rejbrand
Author by

Andreas Rejbrand

MSc in physics, former university-level mathematics teacher (seven years), former PhD student in mathematics, former medical student. Expert Delphi programmer. Win32. Creator of Algosim: https://algosim.org/

Updated on September 25, 2022

Comments

  • Andreas Rejbrand
    Andreas Rejbrand over 1 year

    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!

  • Andreas Rejbrand
    Andreas Rejbrand almost 14 years
    Ah, thank you very much! So it wasn't simply me being stupid, then!
  • Andreas Rejbrand
    Andreas Rejbrand almost 14 years
    BTW: Feel free to remove the question mark next to your name if you want to take credit for the video! :)