Why Can’t I Choose Female “Microsoft Zira” Voice in SpeechSynthesisUtterance()?

Solution for Why Can’t I Choose Female “Microsoft Zira” Voice in SpeechSynthesisUtterance()?
is Given Below:

My following HTML page correctly speaks the text but it is not speaking in a female voice Microsoft Zira Desktop - English (United States). Question: What I may be missing here and how can we make it speak in a female voice?

Remark: I tried this html in MS Edge and Google Chrome multiple times with and without refreshing the page but it keeps speaking with the same male voice. It seems it is ignoring the msg.voice value in the JavaScript below. I am using Windows 10 – that probably should not matter.

<!DOCTYPE html>
<html>
<head>
<script>
    function myFunction() {
  var msg = new SpeechSynthesisUtterance();
      msg.voice = speechSynthesis.getVoices().filter(function(voice) {
    return voice.name == "Microsoft Zira Desktop - English (United States)"})[0];
    msg.text = document.getElementById("testDiv").textContent;
    window.speechSynthesis.speak(msg);
}
</script>
</head>
<body>

<h2>JavaScript in Head</h2>

<div id="testDiv">SQL Managed Instance gives you an instance of SQL Server but removes much of the <b>overhead</b> of managing a <u>virtual machine</u>. Most of the features available in SQL Server are available in SQL Managed Instance. This option is ideal for customers who want to use instance-scoped features and want to move to Azure without rearchitecting their applications.</div>

<button type="button" onclick="myFunction()">Try it</button>

</body>
</html>

UPDATE

Per a suggestion from user @Frazer, I ran speechSynthesis.getVoices() in my google chrome console and got the following results – that does contain Microsoft Zira .... voice:

enter image description here

Observation:

Following this advice, I moved the the <script> block to end of the body block (just before </body>) but still the same male voice. HOWEVER, when I replaced the voice from Microsoft Zira Desktop - English (United States) to Google UK English Female, the following happens: On the first click of Try it button, the speaker is still the default male, but on every subsequent clicks on this button, I correctly get the Google UK English Female voice. Note: The Microsoft Zira Desktop - English (United States) does nothing in the above scenario. This leads me to believe that this technology still is experiential – as mentioned here.


Why Does it Work for Some Browsers?

I have an answer to a similar question here, Why is the voiceschanged event fired on page load?, but I think your situation is sufficiently different to merit a new answer.

First, why does it work sometimes? Because "Microsoft Zira Desktop - English (United States)" is retrieved from the web, through an API call, and this data is not available by the time the next line executes. Basically, you should wait until onvoiceschanged is called before actually calling getVoices() to get the voices.

To quote the docs…

With Chrome however, you have to wait for the event to fire before populating the list, hence the bottom if statement seen below. (Source: MDN WebDocs: SpeechSynthesis.onvoiceschanged) (Emphasis mine.)

If the list doesn’t populate, and you don’t have the female language available, the male will play by default.

Because Constructor `getVoices()` Makes an API Call, Treat it as Asynchronous

Try running your code like so…

var msg = new SpeechSynthesisUtterance();
var voices = window.speechSynthesis.getVoices();

window.speechSynthesis.onvoiceschanged = function() {
    voices = window.speechSynthesis.getVoices();
};

function myFunction() {
    console.log(voices);
      msg.voice = voices.filter(function(voice) {
    return voice.name == "Microsoft Zira - English (United States)"})[0];
    console.log(msg.voice);
    msg.text = document.getElementById("testDiv").textContent;
    window.speechSynthesis.speak(msg);
}

P.S. Here is my own coding example of how I handle the voices loading on a text-to-audio reader: GreenGluon CMS: text-audio.js Also here: PronounceThat.com pronounce-that.js

Add the ‘disabled’ attribute to your button then try this before the /body tag with either Zira or the Chrome voice.

speechSynthesis.onvoiceschanged = () => {
  voices = speechSynthesis.getVoices()
  if (voices.length) document.querySelector("button").disabled = false
}
let voices = speechSynthesis.getVoices()

const  myFunction = () => {
  const msg = new SpeechSynthesisUtterance();
  msg.voice = voices.filter(voice => {
    return voice.name === "Microsoft Zira Desktop - English (United States)"
  })[0];
  msg.text = document.getElementById("testDiv").textContent;
  speechSynthesis.speak(msg);
}