Синтез речи Chrome с более длинными текстами

У меня возникает проблема при попытке использовать API синтеза речи в Chrome 33. Он отлично работает с более коротким текстом, но если я попробую более длинный текст, он просто остановится посередине. После такой остановки синтез речи не работает в 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');

Он перестает говорить в середине второго текста, и после этого я не могу заставить говорить другую страницу.

Это ошибка браузера или какое-то ограничение безопасности?


person Andrey Shchekin    schedule 22.02.2014    source источник
comment
Не уверен, в чем проблема, но определенно воспроизводится на моем компьютере.   -  person Qantas 94 Heavy    schedule 22.02.2014
comment
Вероятно, это ошибка, см. code.google.com/p/chromium / issues / detail? id = 335907. Я обновлю / отвечу, когда появится дополнительная информация.   -  person Andrey Shchekin    schedule 22.02.2014
comment
К вашему сведению: похоже, теперь это исправлено - теперь он переключается на системный голос, если текст слишком длинный, а затем продолжает использовать этот голос даже при разговоре более коротких текстов.   -  person Louis St-Amour    schedule 10.11.2014
comment
Ошибка в Chromium / Google Chrome все еще открыта, хотя # 335907 был объединен с # 369472.   -  person Joel Purra    schedule 18.01.2017
comment
Мое решение, реализованное в расширении Google Chrome Talkie (исходный код) включает разбиение текста на абзацы, а затем разбиение каждого абзаца на предложения / предложения / строки с максимальной длиной 100. Попытка разбить текст там, где в речи есть естественная пауза, например, после запятых, двоеточий и т. Д. Затем каждая часть добавляется в массив и произносится одна за другой. Если пользователь отменяет, оставшиеся части пропускаются. Попробуйте!   -  person Joel Purra    schedule 18.01.2017
comment
Я знаю, что прошло шесть лет, есть мысли по поводу принятого ответа? Я могу подтвердить, что ответ jjsa отлично сработал для меня.   -  person HoldOffHunger    schedule 23.11.2020


Ответы (11)


У меня уже давно была эта проблема с синтезом речи Google Chrome. После некоторого расследования я обнаружил следующее:

  • Прерывание высказываний происходит только тогда, когда голос не является родным,
  • Вырезание обычно происходит между 200–300 символов,
  • Когда он сломается, вы можете разблокировать его, выполнив speechSynthesis.cancel();
  • Событие onend иногда решает не срабатывать. Причудливый обходной путь состоит в том, чтобы console.log () извлекала объект высказывания перед его произнесением. Также я обнаружил, что включение вызова speak в обратный вызов setTimeout помогает сгладить эти проблемы.

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

Обновить

Я сделал этот обходной путь общедоступным по адресу https://gist.github.com/woollsta/2d146f13878a301b36d7#file-chunkify-js. Большое спасибо Бретту Замиру за его вклад.

Функция:

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');
});

Надеюсь, люди сочтут это полезным.

person Peter Woolley    schedule 22.05.2014
comment
Можно подтвердить эту проблему с помощью chrome html5 SpeechSynthesis, часть SpeechSynthesisUtterance иногда не запускается, ваши советы немного помогают. - person user85155; 29.05.2014
comment
Отличная работа! Вы открыты для MIT лицензирования вместо SO по умолчанию? - person Brett Zamir; 05.06.2014
comment
на самом деле, у меня это не работает: / Ошибка в шаблоне фрагмента (с моими изменениями JSLint или без них) - person Brett Zamir; 05.06.2014
comment
@BrettZamir не работает в середине речи или вообще не говорит? Попробуйте выполнить speechSynthesis.cancel(); перед вызовом шаблона фрагментации, чтобы очистить любые речевые высказывания в очереди. Также, если это нарушает середину речи, рассмотрите возможность настройки свойства chunkLength на меньшее значение. Позвольте мне знать, если это помогает. - person Peter Woolley; 05.06.2014
comment
Итак, еще раз спасибо - очень удобно! Как насчет того, чтобы поместить его в Gist по лицензии MIT? - person Brett Zamir; 05.06.2014
comment
У меня все заработало. Во-первых, я изменил . вхождений в регулярном выражении на [\s\S], чтобы улавливать новые строки. Во-вторых, я добавил событие конца через addEventListener на случай, если пользователь добавил свое собственное. В-третьих, я проверяю логическое значение speechUtteranceChunker.cancel, чтобы скрипты могли установить его для полной отмены. - person Brett Zamir; 05.06.2014
comment
Извините, еще один набор модов: я разрешил необязательную настройку modifier, чтобы люди могли добавлять события через addEventListener к фрагментированным высказываниям. Я также избегал фрагментов, если предоставленное высказывание было родным типом - person Brett Zamir; 05.06.2014
comment
Брилл, молодец, добавили GIST на gist.github.com/woollsta/ 2d146f13878a301b36d7 # file-chunkify-js :) - person Peter Woolley; 05.06.2014
comment
Привет! Молодец. Мы используем SpeechSynthesis через Cordova на IOS, где запись некоторых высказываний приводит к умиранию логгера. Мы обнаружили, что просто сохранение ссылки на высказывание где-нибудь в вашем js (например, в массиве) также работает, без спама в журнале! Мы очищаем массив при отмене SpeechSynthesis - поэтому утечки памяти нет. - person Paul Weber; 24.09.2015
comment
Кстати, это причина, по которой console.log это исправить: stackoverflow.com/questions/28839652/ - person Chris Cinelli; 29.02.2016
comment
Ах, как интересно! Что поддерживает ссылку на массив @PaulWeber, так как это также предотвратит сборку мусора. Я не следил за Chrome, поэтому не уверен, существует ли эта проблема. Но я бы посоветовал кому-то создать и поделиться версией, которая разбивается на естественные точки останова, как оригинал, но немного лучше обрабатывает крайние случаи, а также использует технику push и wipe вместо консольного спама. - person Peter Woolley; 01.03.2016
comment
Вы можете просто разбить текст на куски и загрузить их в webSpeech. Я реализовал его на textfromtospeech.com/uk/text-to-voice - person Andrew Fount; 28.01.2017
comment
Как упоминалось в других ответах, есть более простое решение: setInterval(() => { speechSynthesis.pause(); speechSynthesis.resume(); }, 5000); - person joe; 18.08.2020
comment
Это уничтожит выбор голоса для utt, и простая установка newUtt.voice = utt.voice этого не исправит. - person HoldOffHunger; 23.11.2020

Я решил проблему, имея функцию таймера, которая вызывает функции pause () и resume () и снова вызывает таймер. По первому случаю сбрасываю таймер.

    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);
    ...

Кажется, это хорошо работает.

person jjsa    schedule 31.12.2017
comment
Вы сделали мой день! - person Andrea Riva; 10.07.2018
comment
Ничего себе, я не могу поверить, что за это был только 1 голос. Этот ответ пока работает как прелесть для меня, читающего довольно длинные строки. - person saricden; 21.03.2019
comment
Это заслуживает гораздо большего количества голосов. По моему опыту, это оказалось единственным стабильным решением. - person Alex Oleksiiuk; 15.06.2019
comment
Это лучшее решение. Вам не нужно ничего делать, если вы не меняете голос, но если вы это сделаете, это было проще и стабильнее, чем пытаться разбить большой текст. - person wookie924; 30.04.2020
comment
Я обнаружил, что в Chrome Android .pause(), за которым сразу следует .resume(), может останавливать голос. Я заменил его только .resume() в setInterval, и, похоже, он работает нормально (как указано в ответе @MhagnumDw). - person joe; 18.08.2020
comment
Вау, вау, вау, это отличный ответ. Нет икоты каждые 120 символов, как в ответе Питера Вулли. И этот код выше отвечает на window.speechSynthesis.stop() для кнопки паузы, в то время как ответ Питера sort'a захватывает stop() функциональность и просто нарушает работу. Отличный ответ, молодец, спасибо! - person HoldOffHunger; 23.11.2020

Простое и эффективное решение - периодически возобновлять.

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

Вы можете связать это с первым и начальным событиями, поэтому вы будете вызывать резюме только в случае необходимости. Что-то типа:

var utterance = new SpeechSynthesisUtterance();

utterance.onstart = function(event) {
    resumeInfinity();
};

utterance.onend = function(event) {
    clearTimeout(timeoutResumeInfinity);
};

Я обнаружил это случайно!

Надеюсь на эту помощь!

person MhagnumDw    schedule 09.11.2016
comment
до 1428 символов работают нормально, более 1428 символов - нет. странный - person Patrioticcow; 10.02.2017
comment
Я не думаю, что мы сталкиваемся с ограничением количества символов, а скорее с ограничением по времени (см. Другие ответы на этот вопрос и в других местах). Я только что протестировал скрипку, опубликованную на stackoverflow.com/q/42875726/5025060, с более чем 1800 символами на неродном языке. и он работал под Chrome 63.0.3239.132 (официальная сборка) (64-разрядная версия) и Windows 7/64 Pro. - person CODE-REaD; 17.01.2018
comment
Я попробовал ваше решение, и оно мне подходит. Спасибо, что поделился. - person Andrei Bacescu; 24.09.2020
comment
У нас есть коммерческий продукт, в котором мы столкнулись с этой проблемой. Я нашел ваше предложение вызывать .resume () каждую секунду, поэтому я реализовал его, и, похоже, он работает очень хорошо (пока). Спасибо! - person Jeach; 15.01.2021
comment
Кто-нибудь знает, может ли это вызвать утечку памяти, например, если высказывание будет удалено до того, как onend будет запущен? - person Jankapunkt; 22.06.2021

Проблема с ответом Питера в том, что он не работает, когда у вас настроена очередь синтеза речи. Сценарий поместит новый фрагмент в конец очереди и, следовательно, не в порядке. Пример: 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/

Много реквизитов Питеру за идею, а также за регулярное выражение (которое мне еще предстоит освоить). Я уверен, что 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>
person Michael    schedule 07.03.2015

2017, и эта ошибка все еще существует. Я хорошо разбираюсь в этой проблеме, будучи разработчиком отмеченного наградами расширения Chrome Read Aloud. Ладно, шучу по поводу отмеченной наградами части.

  1. Ваша речь застрянет, если она будет длиннее 15 секунд.
  2. Я обнаружил, что Chrome использует 15-секундный таймер простоя, чтобы решить, когда деактивировать страницу событий / фона расширения. Я считаю, что это виноват.

Обходной путь, который я использовал, - это довольно сложный алгоритм разбиения на части, учитывающий знаки препинания. Для латинских языков я установил максимальный размер блока в 36 слов. Код с открытым исходным кодом, если вы хотите: https://github.com/ken107/read-aloud/blob/315f1e1e1d5be6b28ba47fe0c309961025521de516/js/speech.js#L212

Ограничение в 36 слов работает в большинстве случаев, не превышая 15 секунд. Но будут случаи, когда он все равно застревает. Чтобы оправиться от этого, я использую таймер на 16 секунд.

person Sarsaparilla    schedule 22.11.2017
comment
Хороший! Я пришел сюда, потому что работал над аналогичным проектом и столкнулся с этой проблемой. Так здорово видеть, что у вас уже есть рабочие дополнения для Chrome и Firefox! - person Jason O'Neil; 03.07.2018

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

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);
    }
}
person Emile    schedule 07.10.2014

В итоге я разбил текст на части и получил некоторый опыт обработки различных знаков пунктуации, таких как точки, запятые и т. Д. Например, вы не хотите разбивать текст на запятую, если он является частью числа (например, 10 000 долларов) .

Я протестировал его, и, похоже, он работает с произвольно большими наборами входных данных, а также работает не только на рабочем столе, но и на телефонах Android и iPhone.

Настройте страницу github для синтезатора по адресу: https://github.com/unk1911/speech.

Вы можете увидеть это вживую по адресу: http://edeliverables.com/tts/

person user3892260    schedule 17.12.2014
comment
Похоже, что в последней версии Chrome поддерживаются более длинные фрагменты, поэтому больше нет необходимости разбивать текст на более мелкие фрагменты. - person user3892260; 09.12.2015
comment
На самом деле, я беру это обратно, в последних выпусках Chrome более длинные фрагменты поддерживаются только для носителей языка. другие (например, британский динамик) по-прежнему страдают от проблемы, поэтому мне пришлось вернуть чанкер в свой код - person user3892260; 22.04.2016
comment
На моем планшете Android установлено несколько языков, и я обнаружил, что в Chrome Speech Synthesis нет проблем с длительным разговором на иностранном языке (французском), который я пробовал до сих пор. Итак, я подозреваю, что дело не столько в нативности языка, сколько в том, установлен ли голос локально. - person CODE-REaD; 17.01.2018

Как предложил Майкл, решения Питера действительно хороши, за исключением случаев, когда ваш текст находится на разных строках. Майкл создал демо, чтобы лучше проиллюстрировать проблему. - https://jsfiddle.net/1gzkja90/ и предложил другое решение.

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

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

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

Итак, в решении Питера это может выглядеть следующим образом:

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

Но все же есть проблема с отменой выступления. Он просто переходит в другую последовательность и не останавливается.

person mhlavacka    schedule 07.05.2015

Другой совет: сделайте странные вещи с точкой или скажите DOT и не соблюдайте интонацию речи в конце предложения.

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/

person Gino    schedule 17.04.2017

Я хочу сказать, что с помощью расширений и приложений 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});
}

Надеюсь, это кому-то поможет! Это помогло сделать мое приложение работать более функционально и эпично.

person Jack Hales    schedule 08.07.2019

Да, API синтеза Google останавливается в какой-то момент во время произнесения длинного текста.

Мы видим, что последнее событие, событие onpause и onerror для SpeechSynthesisUtterance не срабатывает нормально, когда происходит внезапная остановка, как и событие voiceSynthesis 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;

    }
}
person Jackie Chao    schedule 07.05.2018
comment
это зацикливается вечно - person Alok Rajasukumaran; 01.06.2018