title: Delphi SAPI Text-To-Speech
body: <p>First of all: this is not a duplicate of <a href="https://stackoverflow.com/questions/1021490/delphi-and-sapi">Delphi and SAPI</a>. I have a specific problem with the "SAPI in Delphi" subject.</p>
  
  <p>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</p>
  
  <pre><code>var
    SpVoice: TSpVoice;
  </code></pre>
  
  <p>I can write</p>
  
  <pre><code>SpVoice.Speak('This is an example.', 1);
  </code></pre>
  
  <p>to get <em>asynchronous</em> audio output.</p>
  
  <p><strong><em>First question</em></strong></p>
  
  <p>According to the documentation, I would be able to write</p>
  
  <pre><code>SpVoice.Speak('This is an example.', 0);
  </code></pre>
  
  <p>to get <em>synchronous</em> audio output, but instead I get an EZeroDivide exception. Why's that?</p>
  
  <p><strong><em>Second question</em></strong></p>
  
  <p>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.</p>
  
  <p>To this end, I tried</p>
  
  <pre><code>procedure TForm1.FormClick(Sender: TObject);
  var
    SpVoice: Variant;
  begin
    SpVoice := CreateOleObject('SAPI.SpVoice');
    SpVoice.Speak('this is a test', 0);
  end;
  </code></pre>
  
  <p>which apparently does nothing at all! (Replacing the 0 with 1 gives me the EZeroDivide exception.)</p>
  
  <p><strong><em>Disclaimer</em></strong></p>
  
  <p>I am rather new to COM/OLE automation. I am sorry for any ignorance or stupidity shown by me in this post...</p>
  
  <h1>Update</h1>
  
  <p>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:</p>
  
  <pre><code>procedure TForm1.FormClick(Sender: TObject);
  var
    SpVoice: variant;
  begin
    SpVoice := CreateOleObject('SAPI.SpVoice');
    SpVoice.Speak('This is a text.');
  end;
  </code></pre>
  
  <p>The solution, as presented by the video, is to alter the FPU control word:</p>
  
  <pre><code>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;
  </code></pre>
  
  <p>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!</p>