即使支持 speechSynthesis,它也无法在移动 Safari 上运行

speechSynthesis not working on mobile Safari even though it's supported

我正在尝试使用 speechSynthesis API。它适用于桌面浏览器和移动设备 Chrome,但不适用于移动 Safari。

  const msg = new SpeechSynthesisUtterance("Hello World");
  window.speechSynthesis.speak(msg);

我添加了一些测试,似乎 API 在 Safari 上受支持,是否是权限问题导致它无法正常工作?

  if ("speechSynthesis" in window) {
    alert("yay");
  } else {
    alert("no");
  }

在我这边,问题分解为在移动 Safari 上正确 加载 语音合成。

有一些东西要按顺序检查:

  • 语音加载了吗?
  • 你的系统上安装了语音吗?
  • 话语配置是否正确?
  • 说话功能是从用户交互事件中调用的吗?

以下示例总结了这些检查并适用于 MacOS 桌面浏览器和 iOS Safari:

let _speechSynth
let _voices
const _cache = {}

/**
 * retries until there have been voices loaded. No stopper flag included in this example. 
 * Note that this function assumes, that there are voices installed on the host system.
 */

function loadVoicesWhenAvailable (onComplete = () => {}) {
  _speechSynth = window.speechSynthesis
  const voices = _speechSynth.getVoices()

  if (voices.length !== 0) {
    _voices = voices
    onComplete()
  } else {
    return setTimeout(function () { loadVoicesWhenAvailable(onComplete) }, 100)
  }
}

/**
 * Returns the first found voice for a given language code.
 */

function getVoices (locale) {
  if (!_speechSynth) {
    throw new Error('Browser does not support speech synthesis')
  }
  if (_cache[locale]) return _cache[locale]

  _cache[locale] = _voices.filter(voice => voice.lang === locale)
  return _cache[locale]
}

/**
 * Speak a certain text 
 * @param locale the locale this voice requires
 * @param text the text to speak
 * @param onEnd callback if tts is finished
 */

function playByText (locale, text, onEnd) {
  const voices = getVoices(locale)

  // TODO load preference here, e.g. male / female etc.
  // TODO but for now we just use the first occurrence
  const utterance = new window.SpeechSynthesisUtterance()
  utterance.voice = voices[0]
  utterance.pitch = 1
  utterance.rate = 1
  utterance.voiceURI = 'native'
  utterance.volume = 1
  utterance.rate = 1
  utterance.pitch = 0.8
  utterance.text = text
  utterance.lang = locale

  if (onEnd) {
    utterance.onend = onEnd
  }

  _speechSynth.cancel() // cancel current speak, if any is running
  _speechSynth.speak(utterance)
}

// on document ready
loadVoicesWhenAvailable(function () {
 console.log("loaded") 
})

function speak () {
  setTimeout(() => playByText("en-US", "Hello, world"), 300)
}
<button onclick="speak()">speak</button>

有关代码的详细信息已添加为代码段中的注释。

这可能只是获取声音的时间问题,因此如果我们在加载页面时请求它们,它们将在用户单击“说话”按钮之前准备就绪。

为了简单起见,我没有设置任何超时。

if ( 'speechSynthesis' in window ) {
  speechSynthesis.cancel(); // removes anything 'stuck'
  speechSynthesis.getVoices();
  // Safari loads voices synchronously so now safe to enable
  speakBtn.disabled = false;
}

const speak = () => {
  const utter = new SpeechSynthesisUtterance();
  utter.text = textToSpeak.value || textToSpeak.placeholder;
  speechSynthesis.speak(utter);
};  

speakBtn.addEventListener('click', speak);
<input type="text" id="textToSpeak" placeholder="1, 2, 3">
<button type="button" id="speakBtn" disabled>Speak</button>

这是一个非常基本的示例,没有选择要使用的语音或任何语言。

如果这不起作用,那就是另一个问题。您是否测试过声音在您的设备上有效? “soft mute”是否开启?