Open Bug 1237082 Opened 8 years ago Updated 2 years ago

speechSynthesis.onvoiceschanged event isn't fired

Categories

(Core :: Web Speech, enhancement)

enhancement

Tracking

()

People

(Reporter: donrhummy, Unassigned)

References

()

Details

User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:43.0) Gecko/20100101 Firefox/43.0
Build ID: 20151223140742

Steps to reproduce:

Went here:
http://codepen.io/CreativePunch/pen/sLizk


Actual results:

Get errors and the voices never show


Expected results:

Should have worked like Chrome
It appears there are no voices returned from speechSynthesis.getVoices()
Version: 43 Branch → 46 Branch
Component: Untriaged → Web Speech
Product: Firefox → Core
See example section of https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/onvoiceschanged.  We don't support onvoiceschanged yet.
Severity: normal → enhancement
Status: UNCONFIRMED → NEW
Ever confirmed: true
OS: Unspecified → All
Hardware: Unspecified → All
Summary: Speech Synthesis Demo works in Chrome but not Firefox Nightly → speechSynthesis.onvoiceschanged event isn't fired
Version: 46 Branch → Trunk
(In reply to Makoto Kato [:m_kato] from comment #2)
> See example section of
> https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/
> onvoiceschanged.  We don't support onvoiceschanged yet.

But what does that have to do with not even having any voices? getVoices() returns null.
(In reply to donrhummy from comment #3)
> (In reply to Makoto Kato [:m_kato] from comment #2)
> > See example section of
> > https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/
> > onvoiceschanged.  We don't support onvoiceschanged yet.
> 
> But what does that have to do with not even having any voices? getVoices()
> returns null.

At first, http://codepen.io/CreativePunch/pen/sLizk doesn't work because onvoiceschanged isn't supproted.

When you run (click display) the following JS on scratchpad, does it return 0?

speechSynthesis.getVoices().length

If so, it depends on OS configuration.  If using Windows, you need config SAPI voices via old style control panel.  Our implementation is SAPI only, so we cannot use Windows Store App's voices from Windows 8+ (bug  1235745).
(In reply to Makoto Kato [:m_kato] from comment #4)

> When you run (click display) the following JS on scratchpad, does it return
> 0?
> 
> speechSynthesis.getVoices().length
> 

Actually, the following code does nothing and it's considered the minimum to implement the API:

    var utterance = new SpeechSynthesisUtterance('Hello Treehouse');
    window.speechSynthesis.speak(utterance);

It runs without error but no sound occurs.
(In reply to donrhummy from comment #5)
> (In reply to Makoto Kato [:m_kato] from comment #4)
> 
> > When you run (click display) the following JS on scratchpad, does it return
> > 0?
> > 
> > speechSynthesis.getVoices().length
> > 
> 
> Actually, the following code does nothing and it's considered the minimum to
> implement the API:
> 
>     var utterance = new SpeechSynthesisUtterance('Hello Treehouse');
>     window.speechSynthesis.speak(utterance);
> 
> It runs without error but no sound occurs.

So I think that your system has no voice system.
Blocks: 1254378
Blocks: 1262067
No longer blocks: 1262067
Check that speechd is installed, and kill it before starting Firefox to make sure it is not in a bad state.

I guess things have changed since then, but using only system voices, the voiceschanged event actually fires the first time the domain is loaded.
Afterward, on reload or navigation, it's not fired anymore but the getVoices() is already populated.
Chrome does fire that event every time, after the page's DOMContentLoaded event fired. Would matching that behavior be an option?

voiceschanged will only be fired when new voices are available. When a page is reloaded, or another page in the same domain/process is loaded the voices are available from the start so there is no need for the event. Here is an async voice getter that handles both first and subsequent page loads. It should work in all browsers:

async function getVoices() {
  let voices = speechSymthesis.getVoices();
  if (voices.length) return voices;

  await new Promise(r => speechSynthesis.addEventListener("voiceschanged", r, { once: true }));
  return speechSymthesis.getVoices();
}

I'm not saying that the current behavior is against the specs, I'm just wondering if it's the most user-friendly one.
Honestly, having to write such a boiler plate sounds problematic. I guess the API should have made a .ready Promise available, but I fear that ship has already sailed.
Since Chrome does apparently "fake" the acquisition of the voices to make it async at every page load (getVoices().length will always be 0 before DOMContentLoaded and the voiceschanged event will always fire), people have since written code that assume this behavior:

For instance this quite popular StackOverflow answer does assume Chrome's behavior.
And a quick search for "onvoiceschanged" on Github reveals that a lot of scripts currently do something like

public async getVoiceList(): Promise<SpeechSynthesisVoice[]> {
    if ('onvoiceschanged' in speechSynthesis) {
      await new Promise((resolve, reject) => {
        this.speechSynthesis.addEventListener('voiceschanged', resolve);
      });
    }
    return this.speechSynthesis.getVoices();

(excerpt from https://github.com/MrHassanKhan/nx-angular-nestjs/blob/master/libs/chat-box/src/lib/services/text-to-speech.service.ts)

This will work only the first time in Firefox, but every time in Chrome. That makes it very hard to catch even for tested code bases.

So once again, I'm wondering if it wouldn't be in Firefox's users' interest to actually follow Chrome's behavior here, and "lie" about the voices being available before page load.

Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.