在 JavaScript 中,对于文本到语音,当 voiceschanged 事件被收听时,语音数组没有任何反应? [英] In JavaScript, for text to speech, when the voiceschanged event was being listened to, nothing happened with an array of voices?

查看:20
本文介绍了在 JavaScript 中,对于文本到语音,当 voiceschanged 事件被收听时,语音数组没有任何反应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个文本转语音问题相关,我有以下代码:

首先,我不确定这是否是编写代码的最佳方式,因为它正在初始化,然后将侦听器设置为调用自身".(似乎有点hacky).

First of all, I am not sure if this is the best way to write the code, because it is initializing, and then setting a listener to "call itself". (seems a little bit hacky).

其次,虽然听众得到了通知",但voices.length仍然是0.voices 数组实际上什么也没有发生.我不得不取消注释这一行:

Second, although the listeners got "notified", the voices.length is still 0. Nothing really happens with an array of voices. I had to uncomment this line:

// voices = synth.getVoices();   // this line should not be needed

这样它就可以返回一组voices,但即便如此,第一个单词也没有发音.为什么 synth.getVoices() 需要调用两次,为什么第一个单词没有发音?看起来你只需要调用一次 synth.getVoices().

so that it can come back with an array of voices, but even so, the first word was not pronounced. Why does synth.getVoices() need to be called twice, and why did the first word not get pronounced? It would appear you only have to call synth.getVoices() once.

请注意,如果您将其作为片段进行尝试,则不会发出任何声音(由于 iframe 或安全原因?要听到某些声音,代码需要在开发人员的控制台中运行).

Note that if you try it as a snippet, no voice will be given out (due to iframe or security reasons? To hear something, the code needs to run in developer's console).

(我调试的时候有一个提示:如果所有的声音都得到了,再运行最后3行,第二行和第三行就变成了一样.看来是需要某种语音结束"事件听了,把它们一个一个地序列化——也许使用一个承诺或异步函数.但后来进一步的调试表明,似乎每次都需要一个 SpeechSynthesisUtterance 的新实例,所以我移动了 let msg = new SpeechSynthesisUtterance(); 到最后一个 else { } 的内部并且运行这 3 行没有问题.

(one note when I was debugging: if all the voices were obtained, and the last 3 lines were run again, the second and third line became the same. It looks like some kind of "speech end" event needs to be listened to, to serialize them one by one -- maybe using a promise or async function. But then further debugging showed that it seemed a new instance of SpeechSynthesisUtterance is needed each time, so I moved the let msg = new SpeechSynthesisUtterance(); to inside of the last else { } and running those 3 lines have no problem).

所以让我隐藏原始片段:

So let me hide the original snippet:

let msg, synth, voices;

function foo(phrase) {
  if (!voices) {
    msg = new SpeechSynthesisUtterance();
    synth = window.speechSynthesis;
    voices = synth.getVoices();

    console.log("Waiting 01", voices);
    synth.addEventListener('voiceschanged', function() {
      foo(phrase);
    });
  } else if (voices.length === 0) {
    // this section is needed if foo() is called twice or multiple times in a row initially

    console.log("Waiting 02", voices);

    // voices = synth.getVoices();   // this line should not be needed
    synth.addEventListener('voiceschanged', function() {
      foo(phrase);
    });
  } else {
    console.log("How many voices", voices.length);

    // the voices are ready and could be changed here,
    // but since each system is different, so it won't be
    // changed here:
    // msg.voice = voices[0];
    msg.text = phrase;
    synth.speak(msg);
  }
}

foo("Hello");
foo("World");
foo("a third line");

并显示改进的版本(仍然有同样的问题):

and show the improved version (which still has the same problem):

let msg, synth, voices;

function foo(phrase) {
  if (!voices) {
    synth = window.speechSynthesis;
    voices = synth.getVoices();

    console.log("Waiting 01", voices);
    synth.addEventListener('voiceschanged', function() {
      foo(phrase);
    });
  } else if (voices.length === 0) {
    // this section is needed if foo() is called twice or multiple times in a row initially.
    // synth.getVoices() has been called and we shouldn't need to call it again.
    // but if voices.length is still 0 we just again listen on the voiceschanged event and when ready, call foo(phrase)

    console.log("Waiting 02", voices);

    // voices = synth.getVoices();   // this line should not be needed
    synth.addEventListener('voiceschanged', function() {
      foo(phrase);
    });
  } else {
    let msg = new SpeechSynthesisUtterance();

    console.log("How many voices", voices.length);

    // the voices are ready and could be changed here,
    // but since each system is different, so it won't be
    // changed here:
    // msg.voice = voices[0];
    msg.text = phrase;
    synth.speak(msg);
  }
}

foo("Hello");
foo("World");
foo("a third line");

推荐答案

好的,我想我找到了问题.

OK, I think I found the issue.

问题在于 voices = synth.getVoices();

这是在示例代码中获得声音的推荐方式,但请注意,在类似情况下,对于事件驱动或未来价值"或承诺编程,价值不存在.未来,可能在事件对象中,或提供给回调(提供给处理程序或侦听器"或观察者").(或者我们可能会认为,voices 是一个数组.它不能被填充吗?在这种情况下,答案似乎是否".)

That's the recommended way to get the voices in the sample code, but note that in similar situation for event driven or "future value" or promise programming, the value is not there. It is in the future, possibly in the event object, or provided to the callback (provided to the handler or the "listener" or "observer"). (Or we might think, voices is an array. Can't it just get populated? The answer seems like a "no" in this case.)

在这种情况下,事件中似乎没有 voices.所以我们需要在handler被调用时再次调用voices = synth.getVoices();,所以代码变成:

In this case, the event doesn't seem to have the voices in it. So we need to call voices = synth.getVoices(); again when the handler gets called, so the code becomes:

(更新:我看到的一些示例代码似乎没有正确执行.我们甚至不需要第一次调用 voices = synth.getVoices().当网页会自行初始化,即使页面不使用语音,事件 voiceschanged 也会被触发并调用处理程序.因此最终代码可以第一次调用 synth.getVoices() 删除):

(Update: it seems some sample code I saw didn't do it correctly. We don't even need to call voices = synth.getVoices() the first time. When the webpage initializes itself, even if the page doesn't use voices, the event voiceschanged will get fired and the handler called. So the final code can have the first call to synth.getVoices() removed):

但是如果当我们添加事件监听器,或者当页面已经加载,并且我们运行以下代码片段时,事件 voiceschanged 已经触发,所以我们可以监听,但是我们不会得到通知.答案是:似乎当我们注册监听器时,即使事件已经发生,监听器也总是会收到通知,或者系统无论如何都会通知监听器,这是第一次,类似于 promise.then().但是为了安全起见,如果我们不依赖这个事实,我们真的应该调用 synth.getVoices() 两次.第一次,如果 voices.length0,则监听事件以在准备好时设置它.

But what if when we add the event listener, or when the page already loaded, and we run the following code snippet, the event voiceschanged already fired and so we can listen, but we won't get notified. The answer is: it seems when we register the listener, seems like the listener always get notified even if the event happened already or the system just notify the listener no matter what, for the first time, similar to a promise.then(). But to be safe, if we don't count on this fact, we really should call synth.getVoices() twice. The first time and if voices.length is 0, then listen on the event for the change to set it when ready.

其实一个观察是这样的:即使页面加载了3分钟,如果在调试控制台中,我们做了一个synth.getVoices(),它仍然是一个空数组.只有当我们执行 synth.addEventListener('voiceschanged'... 并等待通知时,它才不会是一个空数组.它实际上类似于量子力学:如果我们不观察它,声音不在那里.但是一旦我们观察它,声音就会在那里.(观察与否真的不应该有声音与否).所以如果我们真的生活在一个虚拟现实的世界中在某些计算机内部……该程序可能以相同的方式运行……如果我们考虑一下为什么在量子力学中,一旦我们观察到某些东西,它就会有所不同).voiceschanged 的行为是,如果我们不观察它,事件就不会发生.但是一旦我们观察到它,事件就会发生.

Actually, one observation is this: even when the page is loaded for 3 minutes, and if in the debug console, we do a synth.getVoices(), it is still an empty array. It won't be an empty array only if we do synth.addEventListener('voiceschanged'... and wait to be notified. It actually is similar to quantum mechanics: if we don't observe it, the voices are not there. But once we observe it, the voices will be there. (observing it or not really should not matter whether the voices are there or not). So if we really live in a world which is a virtual reality inside of some computer... that program might be behaving the same way... if we think about why in quantum mechanics, once we observe something, it is different). The behavior of voiceschanged is, if we don't observe it, the event will not occur. But once we observe it, the event will occur.

另一种让它返回一些声音的方法是通过调试控制台中的 console.log(window.speechSynthesis.getVoices()),然后等待半秒并运行 <再次代码>console.log(window.speechSynthesis.getVoices()).只等待一个事件周期是行不通的.如果我们在一行中执行以下操作,它将不起作用:console.log(window.speechSynthesis.getVoices());设置超时(函数(){console.log(window.speechSynthesis.getVoices());}, 0);

another way to have it return some voices is by console.log(window.speechSynthesis.getVoices()) in the debug console, and wait even just a half second and run console.log(window.speechSynthesis.getVoices()) again. Waiting just one event cycle won't work. If we do the following in one line, it won't work: console.log(window.speechSynthesis.getVoices()); setTimeout(function() { console.log(window.speechSynthesis.getVoices()); }, 0);

const synth = window.speechSynthesis;
let voices;

function foo(phrase) {
  if (!voices) {
    console.log("Waiting 01", voices);

    synth.addEventListener('voiceschanged', function(ev) {
      voices = voices || synth.getVoices();  
      foo(phrase);
    });
  } else {
    let msg = new SpeechSynthesisUtterance(); 

    console.log("How many voices", voices.length);

    // the voices are ready and could be changed here,
    // but since each system is different, so it won't be
    // changed here:
    // msg.voice = voices[0];
    msg.text = phrase;
    synth.speak(msg);
  }
}

foo("Hello");
foo("World");
foo("a third line");

我们甚至可能不会在第一次使用 voices = synth.getVoices();,而只使用 voices = [];synth.getVoices(); 因为如果我们将其设置为 synth.getVoices(),其他阅读代码的编码人员可能会期望它会被填充.

We might even not use voices = synth.getVoices(); the very first time, and just use voices = []; synth.getVoices(); because if we set it to synth.getVoices(), other coders reading the code might have some expectation that it will get populated.

这篇关于在 JavaScript 中,对于文本到语音,当 voiceschanged 事件被收听时,语音数组没有任何反应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆