Обратный вызов API SpeechSynthesis не работает

Я использую API синтеза речи в Google Chrome v34.0.1847.131. API реализован в Chrome начиная с версии 33.

Преобразование текста в речь работает по большей части, за исключением случаев, когда обратный вызов назначается для onend. Например, следующий код:

var message = window.SpeechSynthesisUtterance("Hello world!");
message.onend = function(event) {
    console.log('Finished in ' + event.elapsedTime + ' seconds.');
};
window.speechSynthesis.speak(message);

иногда вызывает onend, а иногда не называет его. Кажется, что время полностью отключено. Когда он вызывается, напечатанный elapsedTime всегда является временем некоторой эпохи, например 1399237888.


comment
просто потому, что спецификации написаны на w3 ... не означает, что они работают или работают точно так же во всех браузерах, особенно если они новые и экспериментальные, такие как синтез речи ... не то чтобы эта возможность была своего рода технологическим прорывом, но это впервые быть доведенным до браузеров.   -  person Muhammad Umer    schedule 12.05.2014
comment
Я проверил, что вы делали, и да, проблема существует ... для меня событие по окончании не сработало ни разу ... и иногда при запуске тоже.   -  person Muhammad Umer    schedule 12.05.2014
comment
Спасибо за проверку. Приятно видеть кого-то еще с этой проблемой. Я предполагаю, что это может быть просто недоработанная реализация, поэтому на этот вопрос может не быть ответа.   -  person huu    schedule 12.05.2014
comment
проверьте это ... по какой-то странной причине, если вы выйдете из объекта utterance message, он будет работать нормально. : D jsfiddle.net/QYw6b   -  person Muhammad Umer    schedule 12.05.2014
comment
Вау, это интересно. event.elapsedTime все еще койка, но кажется, что onend каждый раз стреляет.   -  person huu    schedule 12.05.2014
comment
на самом деле я думаю, что проблема в вызове функции Speak сразу после объявления объекта сообщения… если вы просто выполните это `setTimeout (function () {speechSynthesis.speak (u);}, 100);`, это работает .. .or атака говорит функцию, чтобы щелкнуть событие, она тоже работает нормально.   -  person Muhammad Umer    schedule 12.05.2014
comment
setTimeout(function(){speechSynthesis.speak(u);},1); тоже работает, и его невозможно отличить от простого называть его прямым. Кажется, что API любит жить только внутри обратного вызова. Хотелось бы получить больше информации о том, почему мы наблюдаем такое поведение, но, вероятно, только гуглеры знают наверняка. Если хотите, вы можете написать ответ на этот вопрос, используя предоставленную вами информацию, и я отдаю ей должное :)   -  person huu    schedule 12.05.2014
comment
я также могу найти время   -  person Muhammad Umer    schedule 12.05.2014
comment
Я до сих пор вижу это на Chrome v46 для Windows. Я не помню, чтобы это происходило в Chrome для Android.   -  person Frank Schwieterman    schedule 04.11.2015


Ответы (9)


Хотя я обнаружил, что это работает именно так, я не уверен, что это правильное поведение ...

Во-первых, не вызывайте эту функцию сразу, используйте обратный вызов.

2-й, чтобы получить время, используйте timeStamp вместо elapsedTime. Вы могли бы просто использовать performance.now().

var btn = document.getElementById('btn');
speechSynthesis.cancel()
var u = new SpeechSynthesisUtterance();
u.text = "This text was changed from the original demo.";

var t;
u.onstart = function (event) {
    t = event.timeStamp;
    console.log(t);
};

u.onend = function (event) {
    t = event.timeStamp - t;
    console.log(event.timeStamp);
    console.log((t / 1000) + " seconds");
};

btn.onclick = function () {speechSynthesis.speak(u);};

Демо: http://jsfiddle.net/QYw6b/2/

прошло время, и оба события наверняка сработали.

person Muhammad Umer    schedule 12.05.2014
comment
хотя в этом примере он работает, как только вы используете неанглоязычный голос, он перестает запускать первое событие. - person Zero Dragon; 31.12.2020

Согласно этому комментарию об ошибке, упомянутой в ответ от Кевина Хакансона, это может быть проблема со сборкой мусора. Сохранение высказывания в переменной перед вызовом speak , похоже, помогает:

window.utterances = [];
var utterance = new SpeechSynthesisUtterance( 'hello' );
utterances.push( utterance );
speechSynthesis.speak( utterance );
person techpeace    schedule 11.03.2016
comment
Спасибо за это! Как только я изменил область видимости моей переменной высказывания, у меня больше не было проблемы. Я согласен с тем, что может быть ошибка, связанная с внутренним сборщиком мусора браузера. - person vbguyny; 01.01.2017
comment
Работал! Чтобы было понятно для новичков вроде меня, расширив область действия переменной SpeechSynthesisUtterance, например global, делает свое дело. - person M3RS; 08.02.2018
comment
Что делает толчок? а что такое высказывания? - person Kanerva Peter; 03.08.2018
comment
utterances - это массив, созданный в первой строке. push добавляет элемент в конец массива: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ - person techpeace; 06.08.2018
comment
Работает как шарм! - person Norly Canarias; 07.02.2019
comment
Это решение не сработало для меня. Мой сценарий заключается в том, что я вызываю свою функцию speak_please() из обратного вызова onend (поэтому вызываю speak_please() изнутри speak_please()), и он не дожидается окончания разговора перед вызовом speak_please() - он просто вызывает его немедленно, поэтому он выполняет итерацию по всем div до конца молниеносно. Версия Chrome 75.0.3770.100 (официальная сборка) (64-разрядная версия) - person user1063287; 28.06.2019
comment
На самом деле, я думаю, что моя проблема в том, что onend запускается не только тогда, когда высказывание естественным образом подходит к концу, но также и после synth.cancel(), поэтому каждый раз, когда я отменяю высказывание, я инициирую onend, чтобы произойти - поэтому я быстро создаю и отменяю несколько высказываний, пока не наступит не осталось div. - person user1063287; 28.06.2019
comment
хороший человек! сохранение только текущего высказывания в глобальном window и удаление его после того, как все выступление будет завершено - отлично подходит для меня! - person chestozo; 28.11.2020

Вы можете использовать EventListener для начала и завершения, как я для Speakerbot (http://www.speakerbot.de/).

Здесь мое лицо меняется, когда говорят слова.

newUtt = new SpeechSynthesisUtterance();

newUtt.addEventListener('start', function () {
     console.log('started');
})

newUtt.addEventListener('end', function () {
     console.log('stopped');
})
person fimbim    schedule 03.09.2014

Я обнаружил, что оба предложенных здесь решения не работают в приложении, которое я только что написал. Единственное решение, которое я мог придумать, - это (вроде) занятое ожидание:

function speak( text, onend ) {
  window.speechSynthesis.cancel();
  var ssu = new SpeechSynthesisUtterance( text );
  window.speechSynthesis.speak( ssu );
  function _wait() {
    if ( ! window.speechSynthesis.speaking ) {
      onend();
      return;
    }
    window.setTimeout( _wait, 200 );
  }
  _wait();
}

вы можете найти полный пример в этом коде

person Mapio    schedule 29.11.2015
comment
Мне нравится, как вы запускаете функцию, вызывая cancel(), и это решает мою проблему. Хотя для меня speechSynthesis.speaking верно гораздо дольше, чем следовало бы, так что напряженное ожидание мне не полезно. - person ubershmekel; 29.03.2021

Это похоже на ошибку Chromium, обнаруженную 12 июля 2015 г.

Проблема 509488: Web Speech API: событие конца SpeechSynthesisUtterance объект иногда не отправляется

person Kevin Hakanson    schedule 17.01.2016

напечатайте высказывание, прежде чем говорить, кажется, работает ... Если я удалю консоль, возникнет эта проблема, не знаю почему

console.log("utterance", utterThis);
synth.speak(utterThis);
person yun    schedule 20.05.2016

Я также обнаружил, что единственный способ сделать эту работу надежнее - использовать .cance. Я использую тайм-аут 17 секунд. Все мои записи короче 20 секунд, так что у меня это работает.

utterance.onstart = function (event) {
setTimeout(function(){window.speechSynthesis.cancel();},17000);
};

Раньше я сталкивался с этой проблемой каждые 8-10 попыток отправки сообщений. Как только я добавил .cancel, кажется, что он всегда работает. Я также вызываю тайм-аут набора при вызове.

setTimeout(function(){window.speechSynthesis.speak(utterance);},100);
person Bruce Allen    schedule 08.12.2015

Единственное, что у меня работало, - это избегать голосов, в которых localService означает true. Эти голоса никогда не срабатывали onend, в то время как другие голоса (localService - false) срабатывали onend.

person ubershmekel    schedule 29.03.2021

Кроме того, в Chrome (но не в Safari) обратный вызов никогда не вызывается, если вы пытаетесь произнести пустую строку.

person Guillaume    schedule 07.05.2021