【问题标题】:Chrome Speech Synthesis with longer texts具有较长文本的 Chrome 语音合成
【发布时间】:2014-03-23 18:03:51
【问题描述】:

我在 Chrome 33 中尝试使用语音合成 API 时遇到问题。它可以完美地处理较短的文本,但如果我尝试较长的文本,它只会停在中间。像这样停止一次后,语音合成在 Chrome 中的任何地方都无法工作,直到重新启动浏览器。

示例代码(http://jsfiddle.net/Mdm47/1/):

function speak(text) {
    var msg = new SpeechSynthesisUtterance();
    var voices = speechSynthesis.getVoices();
    msg.voice = voices[10];
    msg.voiceURI = 'native';
    msg.volume = 1;
    msg.rate = 1;
    msg.pitch = 2;
    msg.text = text;
    msg.lang = 'en-US';

    speechSynthesis.speak(msg);
}

speak('Short text');
speak('Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI. Efficiently unleash cross-media information without cross-media value. Quickly maximize timely deliverables for real-time schemas. Dramatically maintain clicks-and-mortar solutions without functional solutions.');
speak('Another short text');

它在第二个文本中间停止说话,之后我无法让任何其他页面说话。

是浏览器错误还是某种安全限制?

【问题讨论】:

  • 不确定这里有什么问题,但绝对可以在我的电脑上重现。
  • 这可能是一个错误,请参阅code.google.com/p/chromium/issues/detail?id=335907。当获得更多信息时,我会更新/回答。
  • 仅供参考:它现在似乎已修复 - 如果文本太长,它现在的作用是切换到系统语音,然后即使在说较短的文本时也会继续使用该语音。
  • 虽然#335907 已合并到#369472 中,但Chromium/Google Chrome 中的错误仍然存​​在。
  • 我在 Google Chrome 扩展程序Talkie (source code) 中实现的解决方案涉及将文本拆分为段落,然后将每个段落拆分为最大长度为 100 的子句/句子/字符串。尝试将文本拆分到语音中自然停顿的地方,例如逗号、冒号等之后。然后将每个部分添加到一个数组中,并一个接一个地朗读。如果用户取消,则跳过其余部分。试试看!

标签: javascript google-chrome speech-synthesis


【解决方案1】:

2017 年,这个错误仍然存​​在。作为屡获殊荣的 Chrome 扩展程序 Read Aloud 的开发者,我碰巧非常了解这个问题。好吧,开个关于获奖部分的玩笑。

  1. 如果超过 15 秒,您的讲话就会卡住。
  2. 我发现 Chrome 使用 15 秒空闲计时器来决定何时停用扩展程序的事件/背景页面。我相信这是罪魁祸首。

我使用的解决方法是一个相当复杂的分块算法,它尊重标点符号。对于拉丁语言,我将最大块大小设置为 36 个单词。代码是开源的,如果你愿意的话:https://github.com/ken107/read-aloud/blob/315f1e1d5be6b28ba47fe0c309961025521de516/js/speech.js#L212

36 个字的限制在大多数情况下都很有效,保持在 15 秒以内。但是在某些情况下它仍然会卡住。为了从中恢复,我使用了 16 秒计时器。

【讨论】:

  • 不错!我来到这里是因为我正在从事一个类似的项目并遇到了这个问题。很高兴看到您已经开始使用 Chrome 和 Firefox 插件了!
【解决方案2】:

new Vue({
  el: "#app",
  data: {
    text: `Collaboratively administrate empowered markets via plug-and-play networks. Dynamically procrastinate B2C users after installed base benefits. Dramatically visualize customer directed convergence without revolutionary ROI. Efficiently unleash cross-media information without cross-media value. Quickly maximize timely deliverables for real-time schemas. Dramatically maintain clicks-and-mortar solutions without functional solutions.`
  },

  methods:{
    stop_reading() {
      const synth = window.speechSynthesis;
      synth.cancel();
    },

    talk() {
      const synth = window.speechSynthesis;
      const textInput = this.text;

      const utterThis = new SpeechSynthesisUtterance(textInput);
      utterThis.pitch = 0;
      utterThis.rate = 1;
      synth.speak(utterThis);

      const resumeInfinity = () => {
        window.speechSynthesis.resume();
        const timeoutResumeInfinity = setTimeout(resumeInfinity, 1000);
      }
      
      utterThis.onstart = () => {
        resumeInfinity();
      };
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <button @click="talk">Speak</button>
  <button @click="stop_reading">Stop</button>
</div>

【讨论】:

    【解决方案3】:

    我想说的是,通过 Chrome 扩展程序和应用程序,我通过使用 chrome.tts 解决了这个非常烦人的问题,因为 chrome.tts 允许您通过浏览器说话,而不是在您关闭时停止说话的窗口窗口。

    使用下面的代码,你可以解决上面的大演讲问题:

    chrome.tts.speak("Abnormally large string, over 250 characters, etc...");
    setInterval(() => { chrome.tts.resume(); }, 100);
    

    我确信这会起作用,但我这样做只是为了安全:

    var largeData = "";
    var smallChunks = largeData.match(/.{1,250}/g);
    for (var chunk of smallChunks) {
      chrome.tts.speak(chunk, {'enqueue': true});
    }
    

    希望这对某人有所帮助!它有助于使 my application 工作得更功能史诗

    【讨论】:

      【解决方案4】:

      是的,谷歌综合 api 会在朗读长文本时停止。

      我们可以看到 SpeechSynthesisUtterance 的 onend 事件、onpause 和 onerror 事件在突然停止时不会正常触发,speechSynthesis onerror 事件也是如此。

      经过多次尝试,发现speechSynthesis.paused 可以正常工作,speechSynthesis.resume() 可以帮助恢复说话。

      因此我们只需要一个计时器来检查说话过程中的暂停状态,并调用 SpeechSynthesis.resume() 继续。 间隔应该足够小,以防止继续讲话时出现故障。

      let timer = null;
      let reading = false;
      
      let readText = function(text) {
      
          if (!reading) {
              speechSynthesis.cancel();
              if (timer) {
                  clearInterval(timer);
              }
              let msg = new SpeechSynthesisUtterance();
              let voices = window.speechSynthesis.getVoices();
              msg.voice = voices[82];
              msg.voiceURI = 'native';
              msg.volume = 1; // 0 to 1
              msg.rate = 1.0; // 0.1 to 10
              msg.pitch = 1; //0 to 2
              msg.text = text;
              msg.lang = 'zh-TW';
      
              msg.onerror = function(e) {
                  speechSynthesis.cancel();
                  reading = false;
                  clearInterval(timer);
              };
      
              msg.onpause = function(e) {
                  console.log('onpause in ' + e.elapsedTime + ' seconds.');
              }            
      
              msg.onend = function(e) {
                  console.log('onend in ' + e.elapsedTime + ' seconds.');
                  reading = false;
                  clearInterval(timer);
              };
      
              speechSynthesis.onerror = function(e) {
                  console.log('speechSynthesis onerror in ' + e.elapsedTime + ' seconds.');
                  speechSynthesis.cancel();
                  reading = false;
                  clearInterval(timer);
              };
      
              speechSynthesis.speak(msg);
      
              timer = setInterval(function(){
                  if (speechSynthesis.paused) {
                      console.log("#continue")
                      speechSynthesis.resume();
                  }
      
              }, 100);
      
              reading = true;
      
          }
      }
      

      【讨论】:

      • 这是永远循环的
      【解决方案5】:

      我已经解决了这个问题,同时拥有一个调用 pause() 和 resume() 函数并再次调用计时器的计时器函数。在 onend 事件中,我清除计时器。

          var myTimeout;
          function myTimer() {
              window.speechSynthesis.pause();
              window.speechSynthesis.resume();
              myTimeout = setTimeout(myTimer, 10000);
          }
          ...
              window.speechSynthesis.cancel();
              myTimeout = setTimeout(myTimer, 10000);
              var toSpeak = "some text";
              var utt = new SpeechSynthesisUtterance(toSpeak);
              ...
              utt.onend =  function() { clearTimeout(myTimeout); }
              window.speechSynthesis.speak(utt);
          ...
      

      这似乎运作良好。

      【讨论】:

      • 你成就了我的一天!
      • 哇,我不敢相信这只有 1 票。到目前为止,这个答案对我阅读相当长的字符串来说是一种魅力。
      • 这应该得到更多的支持。根据我的经验,这是唯一稳定的解决方案。
      • 这是最好的解决方案。如果你不改变声音,你不需要做任何事情,但如果你这样做了,这比尝试对大文本进行分块更简单、更稳定。
      • 我发现在 Chrome Android 上,.pause() 后跟 .resume() 可以停止声音。我在setInterval 中只用.resume() 替换了它,它似乎工作正常(如@MhagnumDw 的回答所示)。
      【解决方案6】:

      其他建议用点做奇怪的事情或说点,不尊重句末的语音语调。

      var CHARACTER_LIMIT = 200;
      var lang = "en";
      
      var text = "MLA format follows the author-page method of in-text citation. This means that the author's last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author's name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence. Joe waited for the train. The train was late. Mary and Samantha took the bus.";
      
          speak(text, lang)
      
          function speak(text, lang) {
      
            //Support for multipart text (there is a limit on characters)
            var multipartText = [];
      
            if (text.length > CHARACTER_LIMIT) {
      
              var tmptxt = text;
      
              while (tmptxt.length > CHARACTER_LIMIT) {
      
                //Split by common phrase delimiters
                var p = tmptxt.search(/[:!?.;]+/);
                var part = '';
      
                //Coludn't split by priority characters, try commas
                if (p == -1 || p >= CHARACTER_LIMIT) {
                  p = tmptxt.search(/[,]+/);
                }
      
                //Couldn't split by normal characters, then we use spaces
                if (p == -1 || p >= CHARACTER_LIMIT) {
      
                  var words = tmptxt.split(' ');
      
                  for (var i = 0; i < words.length; i++) {
      
                    if (part.length + words[i].length + 1 > CHARACTER_LIMIT)
                      break;
      
                    part += (i != 0 ? ' ' : '') + words[i];
      
                  }
      
                } else {
      
                  part = tmptxt.substr(0, p + 1);
      
                }
      
                tmptxt = tmptxt.substr(part.length, tmptxt.length - part.length);
      
                multipartText.push(part);
                //console.log(part.length + " - " + part);
      
              }
      
              //Add the remaining text
              if (tmptxt.length > 0) {
                multipartText.push(tmptxt);
              }
      
            } else {
      
              //Small text
              multipartText.push(text);
            }
      
      
            //Play multipart text
            for (var i = 0; i < multipartText.length; i++) {
      
              //Use SpeechSynthesis
              //console.log(multipartText[i]);
      
              //Create msg object
              var msg = new SpeechSynthesisUtterance();
              //msg.voice = profile.systemvoice;
              //msg.voiceURI = profile.systemvoice.voiceURI;
              msg.volume = 1; // 0 to 1
              msg.rate = 1; // 0.1 to 10
              // msg.rate = usersetting || 1; // 0.1 to 10
              msg.pitch = 1; //0 to 2*/
              msg.text = multipartText[i];
              msg.speak = multipartText;
              msg.lang = lang;
              msg.onend = self.OnFinishedPlaying;
              msg.onerror = function (e) {
                console.log('Error');
                console.log(e);
              };
              /*GC*/
              msg.onstart = function (e) {
                var curenttxt = e.currentTarget.text;
                console.log(curenttxt);
                //highlight(e.currentTarget.text);
                //$('#showtxt').text(curenttxt);
                //console.log(e);
              };
              //console.log(msg);
              speechSynthesis.speak(msg);
      
            }
      
          }
      

      https://jsfiddle.net/onigetoc/9r27Ltqz/

      【讨论】:

        【解决方案7】:

        一个简单有效的解决方案是定期恢复。

        function resumeInfinity() {
            window.speechSynthesis.resume();
            timeoutResumeInfinity = setTimeout(resumeInfinity, 1000);
        }
        

        您可以将其与 onend 和 onstart 事件相关联,因此您只会在必要时调用简历。比如:

        var utterance = new SpeechSynthesisUtterance();
        
        utterance.onstart = function(event) {
            resumeInfinity();
        };
        
        utterance.onend = function(event) {
            clearTimeout(timeoutResumeInfinity);
        };
        

        我偶然发现了这个!

        希望对您有所帮助!

        【讨论】:

        • 最多 1428 个字符可以正常工作,超过 1428 个字符则不行。奇怪
        • 我认为我们面临的不是字符限制,而是时间限制(请参阅此问题和其他地方的其他答案)。我刚刚测试了在stackoverflow.com/q/42875726/5025060 发布的小提琴,其中包含超过 1800 个字符的非本地语言,它在 Chrome 63.0.3239.132(官方版本)(64 位)和 Windows 7/64 Pro 下工作。
        • 我试过你的解决方案,对我来说效果很好。感谢分享。
        • 我们有一个商业产品困扰着我们这个问题。我发现您提出的每秒调用 .resume() 的建议,所以我实现了它,它似乎工作得很好(到目前为止)。谢谢!
        • 有谁知道,这是否会导致内存泄漏,例如如果在onend 触发之前删除了话语?
        【解决方案8】:

        正如 Michael 所建议的那样,Peter 的解决方案非常棒,除非您的文本位于不同的行上。 Michael 创建了演示以更好地说明问题。 - https://jsfiddle.net/1gzkja90/ 并提出了另一个解决方案。

        添加一个可能更简单的方法来解决这个问题是从 Peter 的解决方案中的 textarea 中删除换行符,它工作得很好。

        //javascript
        var noLineBreaks = document.getElementById('mytextarea').replace(/\n/g,'');
        
        //jquery
        var noLineBreaks = $('#mytextarea').val().replace(/\n/g,'');

        所以在彼得的解决方案中,它可能看起来如下:

        utterance.text = $('#mytextarea').val().replace(/\n/g,'');

        但是取消演讲还是有问题。它只是进入另一个序列并且不会停止。

        【讨论】:

          【解决方案9】:

          彼得回答的问题是,当您设置语音合成队列时,它不起作用。该脚本会将新块放在队列的末尾,因此会出现乱序。示例:https://jsfiddle.net/1gzkja90/

          <script type='text/javascript' src='http://code.jquery.com/jquery-2.1.0.js'></script>
          <script type='text/javascript'>    
              u = new SpeechSynthesisUtterance();
              $(document).ready(function () {
                  $('.t').each(function () {
                      u = new SpeechSynthesisUtterance($(this).text());
          
                      speechUtteranceChunker(u, {
                          chunkLength: 120
                      }, function () {
                          console.log('end');
                      });
                  });
              });
               /**
               * Chunkify
               * Google Chrome Speech Synthesis Chunking Pattern
               * Fixes inconsistencies with speaking long texts in speechUtterance objects 
               * Licensed under the MIT License
               *
               * Peter Woolley and Brett Zamir
               */
              var speechUtteranceChunker = function (utt, settings, callback) {
                  settings = settings || {};
                  var newUtt;
                  var txt = (settings && settings.offset !== undefined ? utt.text.substring(settings.offset) : utt.text);
                  if (utt.voice && utt.voice.voiceURI === 'native') { // Not part of the spec
                      newUtt = utt;
                      newUtt.text = txt;
                      newUtt.addEventListener('end', function () {
                          if (speechUtteranceChunker.cancel) {
                              speechUtteranceChunker.cancel = false;
                          }
                          if (callback !== undefined) {
                              callback();
                          }
                      });
                  }
                  else {
                      var chunkLength = (settings && settings.chunkLength) || 160;
                      var pattRegex = new RegExp('^[\\s\\S]{' + Math.floor(chunkLength / 2) + ',' + chunkLength + '}[.!?,]{1}|^[\\s\\S]{1,' + chunkLength + '}$|^[\\s\\S]{1,' + chunkLength + '} ');
                      var chunkArr = txt.match(pattRegex);
          
                      if (chunkArr[0] === undefined || chunkArr[0].length <= 2) {
                          //call once all text has been spoken...
                          if (callback !== undefined) {
                              callback();
                          }
                          return;
                      }
                      var chunk = chunkArr[0];
                      newUtt = new SpeechSynthesisUtterance(chunk);
                      var x;
                      for (x in utt) {
                          if (utt.hasOwnProperty(x) && x !== 'text') {
                              newUtt[x] = utt[x];
                          }
                      }
                      newUtt.addEventListener('end', function () {
                          if (speechUtteranceChunker.cancel) {
                              speechUtteranceChunker.cancel = false;
                              return;
                          }
                          settings.offset = settings.offset || 0;
                          settings.offset += chunk.length - 1;
                          speechUtteranceChunker(utt, settings, callback);
                      });
                  }
          
                  if (settings.modifier) {
                      settings.modifier(newUtt);
                  }
                  console.log(newUtt); //IMPORTANT!! Do not remove: Logging the object out fixes some onend firing issues.
                  //placing the speak invocation inside a callback fixes ordering and onend issues.
                  setTimeout(function () {
                      speechSynthesis.speak(newUtt);
                  }, 0);
              };
          </script>
          <p class="t">MLA format follows the author-page method of in-text citation. This means that the author's last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author's name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence.</p>
          <p class="t">Joe waited for the train.</p>
          <p class="t">The train was late.</p>
          <p class="t">Mary and Samantha took the bus.</p>
          

          在我的情况下,答案是在将字符串添加到队列之前“分块”字符串。见这里:http://jsfiddle.net/vqvyjzq4/

          Peter 的想法和正则表达式(我还没有征服)的许多道具。我确信 javascript 可以被清理,这更像是一个概念证明。

          <script type='text/javascript' src='http://code.jquery.com/jquery-2.1.0.js'></script>
          <script type='text/javascript'>    
              var chunkLength = 120;
              var pattRegex = new RegExp('^[\\s\\S]{' + Math.floor(chunkLength / 2) + ',' + chunkLength + '}[.!?,]{1}|^[\\s\\S]{1,' + chunkLength + '}$|^[\\s\\S]{1,' + chunkLength + '} ');
          
              $(document).ready(function () {
                  var element = this;
                  var arr = [];
                  var txt = replaceBlank($(element).text());
                  while (txt.length > 0) {
                      arr.push(txt.match(pattRegex)[0]);
                      txt = txt.substring(arr[arr.length - 1].length);
                  }
                  $.each(arr, function () {
                      var u = new SpeechSynthesisUtterance(this.trim());
                      window.speechSynthesis.speak(u);
                  });
              });
          </script>
          <p class="t">MLA format follows the author-page method of in-text citation. This means that the author's last name and the page number(s) from which the quotation or paraphrase is taken must appear in the text, and a complete reference should appear on your Works Cited page. The author's name may appear either in the sentence itself or in parentheses following the quotation or paraphrase, but the page number(s) should always appear in the parentheses, not in the text of your sentence.</p>
          <p class="t">Joe waited for the train.</p>
          <p class="t">The train was late.</p>
          <p class="t">Mary and Samantha took the bus.</p>
          

          【讨论】:

            【解决方案10】:

            我最终将文本分块,并在处理各种标点符号(如句号、逗号等)方面有了一些智能。例如,如果它是数字的一部分,您不想将文本拆分为逗号(即, 10,000 美元)。

            我已经对其进行了测试,它似乎适用于任意大的输入集,而且它似乎不仅适用于桌面,而且适用于 Android 手机和 iphone。

            https://github.com/unk1911/speech为合成器设置一个github页面

            您可以在以下位置看到它:http://edeliverables.com/tts/

            【讨论】:

            • 似乎在最新版本的chrome中支持更长的块,因此不再需要将文本分成更小的块。
            • 实际上我收回了它,在最新的 chrome 版本中,只有母语为美国的人支持更长的块。其他人(例如英国人)仍然存在这个问题,所以我不得不把分块器放回我的代码中
            • 我的Android平板电脑安装了多种语言,我发现Chrome Speech Synthesis用外语(法语)说话时间更长没有问题,到目前为止我已经尝试过了。所以,我怀疑与其说是语言的本地性,不如说是语音是否安装在本地。
            【解决方案11】:

            这是我最后的结果,它只是将我的句子拆分为句点“。”

            var voices = window.speechSynthesis.getVoices();
            
            var sayit = function ()
            {
                var msg = new SpeechSynthesisUtterance();
            
                msg.voice = voices[10]; // Note: some voices don't support altering params
                msg.voiceURI = 'native';
                msg.volume = 1; // 0 to 1
                msg.rate = 1; // 0.1 to 10
                msg.pitch = 2; //0 to 2
                msg.lang = 'en-GB';
                msg.onstart = function (event) {
            
                    console.log("started");
                };
                msg.onend = function(event) {
                    console.log('Finished in ' + event.elapsedTime + ' seconds.');
                };
                msg.onerror = function(event)
                {
            
                    console.log('Errored ' + event);
                }
                msg.onpause = function (event)
                {
                    console.log('paused ' + event);
            
                }
                msg.onboundary = function (event)
                {
                    console.log('onboundary ' + event);
                }
            
                return msg;
            }
            
            
            var speekResponse = function (text)
            {
                speechSynthesis.cancel(); // if it errors, this clears out the error.
            
                var sentences = text.split(".");
                for (var i=0;i< sentences.length;i++)
                {
                    var toSay = sayit();
                    toSay.text = sentences[i];
                    speechSynthesis.speak(toSay);
                }
            }
            

            【讨论】:

              【解决方案12】:

              我在使用 Google Chrome 语音合成时遇到这个问题已经有一段时间了。经过一番调查,我发现了以下内容:

              • 话语中断仅在语音不是本地语音时发生
              • 剪切通常发生在200-300个字符之间
              • 当它崩溃时,你可以通过 speechSynthesis.cancel(); 解冻它
              • 'onend' 事件有时会决定不触发。一个古怪的解决方法是在说出它之前 console.log() 输出话语对象。此外,我发现将 speak 调用包装在 setTimeout 回调中有助于解决这些问题。

              针对这些问题,我编写了一个克服字符限制的函数,将文本分成更小的话语,然后一个接一个地播放。显然,有时您会听到一些奇怪的声音,因为句子可能会被分成两个单独的话语,每个话语之间会有一小段时间延迟,然而代码会尝试在标点符号处分割这些点,以使声音的中断不太明显。

              更新

              我已在 https://gist.github.com/woollsta/2d146f13878a301b36d7#file-chunkify-js 上公开了此解决方法。非常感谢 Brett Zamir 的贡献。

              功能:

              var speechUtteranceChunker = function (utt, settings, callback) {
                  settings = settings || {};
                  var newUtt;
                  var txt = (settings && settings.offset !== undefined ? utt.text.substring(settings.offset) : utt.text);
                  if (utt.voice && utt.voice.voiceURI === 'native') { // Not part of the spec
                      newUtt = utt;
                      newUtt.text = txt;
                      newUtt.addEventListener('end', function () {
                          if (speechUtteranceChunker.cancel) {
                              speechUtteranceChunker.cancel = false;
                          }
                          if (callback !== undefined) {
                              callback();
                          }
                      });
                  }
                  else {
                      var chunkLength = (settings && settings.chunkLength) || 160;
                      var pattRegex = new RegExp('^[\\s\\S]{' + Math.floor(chunkLength / 2) + ',' + chunkLength + '}[.!?,]{1}|^[\\s\\S]{1,' + chunkLength + '}$|^[\\s\\S]{1,' + chunkLength + '} ');
                      var chunkArr = txt.match(pattRegex);
              
                      if (chunkArr[0] === undefined || chunkArr[0].length <= 2) {
                          //call once all text has been spoken...
                          if (callback !== undefined) {
                              callback();
                          }
                          return;
                      }
                      var chunk = chunkArr[0];
                      newUtt = new SpeechSynthesisUtterance(chunk);
                      var x;
                      for (x in utt) {
                          if (utt.hasOwnProperty(x) && x !== 'text') {
                              newUtt[x] = utt[x];
                          }
                      }
                      newUtt.addEventListener('end', function () {
                          if (speechUtteranceChunker.cancel) {
                              speechUtteranceChunker.cancel = false;
                              return;
                          }
                          settings.offset = settings.offset || 0;
                          settings.offset += chunk.length - 1;
                          speechUtteranceChunker(utt, settings, callback);
                      });
                  }
              
                  if (settings.modifier) {
                      settings.modifier(newUtt);
                  }
                  console.log(newUtt); //IMPORTANT!! Do not remove: Logging the object out fixes some onend firing issues.
                  //placing the speak invocation inside a callback fixes ordering and onend issues.
                  setTimeout(function () {
                      speechSynthesis.speak(newUtt);
                  }, 0);
              };
              

              如何使用它...

              //create an utterance as you normally would...
              var myLongText = "This is some long text, oh my goodness look how long I'm getting, wooooohooo!";
              
              var utterance = new SpeechSynthesisUtterance(myLongText);
              
              //modify it as you normally would
              var voiceArr = speechSynthesis.getVoices();
              utterance.voice = voiceArr[2];
              
              //pass it into the chunking function to have it played out.
              //you can set the max number of characters by changing the chunkLength property below.
              //a callback function can also be added that will fire once the entire text has been spoken.
              speechUtteranceChunker(utterance, {
                  chunkLength: 120
              }, function () {
                  //some code to execute when done
                  console.log('done');
              });
              

              希望人们觉得这很有用。

              【讨论】:

              • @BrettZamir 是在演讲中失败,还是根本不说话?在调用分块模式以清除任何排队的语音之前,请尝试执行 speechSynthesis.cancel();。此外,如果它中断了语音,请考虑将chunkLength 属性调整为更小的值。让我知道这是否有帮助。
              • 您好!好工作。我们在IOS上通过cordova使用speechSynthesis,记录一些话语会导致记录器死亡。我们发现,只需在 js 中的某处(如数组)存储对话语的引用也可以,而不会向日志发送垃圾邮件!我们在 SpeechSynthesis 取消时清理了数组 - 所以没有内存泄漏。
              • 顺便说一句,这就是console.log修复它的原因:stackoverflow.com/questions/28839652/…
              • 您可以将文本拆分成块并将它们加载到 webSpeech 中。我在textfromtospeech.com/uk/text-to-voice 上实现了它
              • 正如其他答案中提到的,有一个更简单的解决方案:setInterval(() =&gt; { speechSynthesis.pause(); speechSynthesis.resume(); }, 5000);
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-07-02
              • 1970-01-01
              • 2023-03-03
              • 1970-01-01
              • 2020-04-26
              • 1970-01-01
              • 2017-12-05
              相关资源
              最近更新 更多