语音合成在移动 Safari 上不起作用,即使它受支持

Eva*_*nss 5 javascript text-to-speech mobile-safari

我正在尝试使用语音合成 API。它适用于桌面浏览器和移动 Chrome,但不适用于移动 Safari。

  const msg = new SpeechSynthesisUtterance("Hello World");
  window.speechSynthesis.speak(msg);
Run Code Online (Sandbox Code Playgroud)

我添加了一个小测试,似乎 Safari 支持该 API,这可能是权限问题导致它不起作用吗?

  if ("speechSynthesis" in window) {
    alert("yay");
  } else {
    alert("no");
  }
Run Code Online (Sandbox Code Playgroud)

Jan*_*nkt 2

就我而言,问题归结为在移动 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)
}
Run Code Online (Sandbox Code Playgroud)
<button onclick="speak()">speak</button>
Run Code Online (Sandbox Code Playgroud)

代码的详细信息将作为注释添加到代码片段中。