How to make SAPI5 speak on IIS

Forget it! Microsoft Sam and other Microsoft supplied SAPI5 voices have a couple of major issues, making them work on the Internet Information Services 6 resembles to voodoo magic. However, it is doable. I shall describe the way to make your website speak, but you will not like the solution.

Registry permissions

First issue I am going to describe is registry permissions. Let us perform a simple test. The following example is a short ASP JScript code that lists all the SAPI5 voices found. Save the following example to a file with ASP extension (“test.asp” for example) inside your C:\Inetpub\wwwroot folder:

<script runat="Server" language="JScript">
var
  voice = new ActiveXObject("SAPI.SpVoice"),
  voices, i, n;

voices = voice.GetVoices("");
n = voices.Count;
for (i = 0; i < n; i++) {
  var
     v = voices.Item(i);

  Response.Write("Voice ID: " + v.Id + ", Title: " +
     v.GetDescription()+ "</br>");
}
</script>

Example 1 Generate a list of installed SAPI5 voices using ASP script

When you try to load the file using URL address (“http://localhost/test.asp” for example) in your browser, you get the following error:

HTTP 500.100 - Internal Server Error - ASP error
Internet Information Services

Error 0x80045039

After doing some research, I found the sperror.h file, giving the following error description:

SPERR_NO_MORE_ITEMS
When enumerating items, the requested index is greater than
the count of items.

To put it in another way, SAPI5 core did not find any voices.

Let us continue our test. Go to the IIS’s configuration and temporary disable the anonymous access to the sample ASP file forcing you to authenticate when you reload it. Provide credentials of any user with administrative privilege and voila – it works now.

When things work for one user and not for another on the very same computer, the usual cause is permission problems. This led me to investigate the behaviour of SAPI5 core further and to discover the following:

Any process trying to make use of SAPI5 voices requires both: read and write access to the HKLM\SOFTWARE\Microsoft\Speech key and its sub-keys.

Therefore, if you grant the IUSR_machine user full access to this registry key, restart the World Wide Web Publishing service the Example 1 above starts working.

Microsoft Sam loves to interact

In previous chapter, we have seen the example, how to list the installed SAPI5 voices. Let us take a look, how to make it speak next. The example below is a simple script, which opens a TestSAPI.wav file in the _private folder. Make sure the IUSR_machine user has write permission to the _private folder. It searches for all English SAPI5 voices installed next, ordering them by preferred attributes. Finally, it selects the first appropriate voice and tries to synthesize the “Hello world!” sentence into the WAV file.

<script runat="Server" language="JScript">
var
  output = new ActiveXObject("SAPI.SpFileStream"),
  format = new ActiveXObject("SAPI.SpAudioFormat"),
  voice = new ActiveXObject("SAPI.SpVoice"),
  voices;

// Prepare the output WAV file.
format.Type = 18 /*SAFT16kHz16BitMono*/;
output.Format = format;
output.Open(Server.MapPath("_private/TestSAPI.wav"),
  3 /*SSFMCreateForWrite*/);

try {
  // Find all installed English voices (language code 9).
  // Male adult speakers preferably.
  voices = voice.GetVoices("Language=9",
     "Gender=Male;Age=Adult");
  if (voices.Count) {
     // Select the first voice.
     voice.Voice = voices.Item(0);

     // Output goes to WAV file.
     voice.AudioOutputStream = output;

     // Read the text.
     voice.Speak("<SAPI>Hello world!</SAPI>",
       8 /*SVSFIsXML*/);

     Response.Write("Success!");
  } else
     Response.Write("No English SAPI5 voices found.");
} catch (e) {
  throw e;
} finally {
  output.Close();
}
</script>

Example 2 Read a sentence to a WAV file

Suppose your only English SAPI5 voice installed is Microsoft Sam. When you try to run the above script entering its URL in your browser, you get the following error:

HTTP 500.100 - Internal Server Error - ASP error
Internet Information Services

Error 0x800A0046 Permission denied

The error description pinpoints the line with the voice.Speak() method call. Providing you granted the write permission to the _private folder to IUSR_machine user correctly, the folder should contain an almost empty TestSAPI.wav file. Therefore, this “Permission denied” error does not consequence insufficient access right to the _private folder.

We can try the same trick as in previous chapter, by disabling the anonymous access to the sample ASP file, and restarting the World Wide Web Publishing service. After reloading the example and providing the credentials of any user with administrative privileges, it either works either returns an error:

HTTP 500.100 - Internal Server Error - ASP error
Internet Information Services

Error 0x80004005 Unspecified error

Make sure to restart the World Wide Web Publishing service after each retry – SAPI5 and/or Microsoft Sam have some issues beyond ones described here.

The difference between working credentials or non-working ones is the interactivity of the user.

Microsoft Sam voice works only when run by the same user, who is also logged in locally – namely the interactive user.

I assume the Microsoft Sam voice wants to access the sound card, even if you configure the output to a WAV file or a memory stream. Other proprietary SAPI5 voices do not exhibit the same behaviour. It is specific to Microsoft supplied voices.

Therefore, if you would like to make use of the Microsoft Sam voice on your website, you should:

  1. Create a user X on the web server, copying the settings from IUSR_machine user.
  2. Configure your website to run as user X.
  3. Grant the user X read and write access to the HKLM\SOFTWARE\Microsoft\Speech registry key (see the Registry permissions chapter above).
  4. Configure the web server to auto-login the user X locally after each server restart.

… or forget about the Microsoft Sam voice and get some third party one.

3 thoughts on “How to make SAPI5 speak on IIS”

  1. Hello

    A very nice article.I followed your steps and most of the stuff works.
    But I keep on getting an eror on voice.speak line.The error is following:-

    Microsoft VBScript runtime error ‘800a01ad’

    ActiveX component can’t create object

    /elearning/student_login.asp, line 9

    Could you please help me on that.
    Regards,
    Jatin Dhawan

  2. Dear Jatin,

    Please, see the Microsoft KB194801 for some clues regarding the 800a01ad error first.

    To locate the cause, try putting the relevant section of VBScript code to a standalone script file (i.e. test.vbs) and run it using cscript.exe utility.
    If you can make the VBS file run and perform some synthesis, then it’s definitely IIS’s IUSR_xxx permission problem.

    Best regards,
    Simon

  3. You also get 0x800A0046 Permission denied when you setup two websites to run as different user and both using SAPI.SpVoice. The first website to get started works, the second one reports 0x800A0046 on Speak() method call. If the application pool of the first one is stopped, the second one starts working.
    This leads me to a suspection, that Microsoft SAPI has a built-in user exclusion: only one user can use SAPI5 at once.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.