Skip to content

Instantly share code, notes, and snippets.

@xfenix
Last active August 7, 2018 20:13
Show Gist options
  • Save xfenix/58a739479c16aeaeb8300e83917b43d9 to your computer and use it in GitHub Desktop.
Save xfenix/58a739479c16aeaeb8300e83917b43d9 to your computer and use it in GitHub Desktop.
Browser speech generator (with chrome chunk bug fix)
(function() {
// article voice synth
var SpeechGenerator = function() {
var that = this;
this.STATES = { STOP: 0, RUN: 1, PAUSE: 2 };
this.chunkSize = 190;
this.$root = $('.content');
this.subTitleClass = 'second_title';
this.status = this.STATES.STOP;
this.language = 'ru-RU';
this.matcher = new RegExp('(.{1,' + this.chunkSize + '}[,.!?]|.{1,' + this.chunkSize + '})', 'g');
this.textParts = [];
this.engine = window.speechSynthesis;
this.init = function() {
if(this.isEnabled()) {
this.textParts = this.prepareText();
this.bindEvents();
}
};
this.isEnabled = function() {
if('speechSynthesis' in window) {
var i, voices = this.engine.getVoices();
for(i in voices) {
if(voices[i].lang == this.language) {
return true;
}
}
}
return false;
}
this.prepareText = function() {
// сначала собираем текст
var i, j, matchParts, fullText = '', resultParts = [],
textParts = [
this.$root.find('h1').text(),
this.$root.find('.' + this.subTitleClass).text(),
];
// подготовим текст
this.$root.find('p').each(function() {
var localText = $.trim($(this).text());
if(localText.length > 0 && !$(this).hasClass(that.subTitleClass)) {
fullText += localText + ' ';
}
});
// разделяем на части по точкам обязательно
// иначе текст невозможно слушать
textParts = $.merge(textParts, fullText.split('.'));
for(i in textParts) {
// а потом каждый элемент разделяем по регулярке,
// на случай больших предложений
if(textParts[i].length > this.chunkSize) {
matchParts = textParts[i].match(this.matcher);
for(j in matchParts) {
resultParts.push($.trim(matchParts[j]));
}
} else {
resultParts.push($.trim(textParts[i]));
}
}
return resultParts;
};
this.bindEvents = function() {
// запускаем синтез при клике на play
$('#generateme').on('click', function() {
if(that.status == that.STATES.STOP) {
that.runGeneration();
} else if(that.status == that.STATES.PAUSE) {
that.resume();
} else {
that.pause();
}
return false;
});
// при закрытии страницы, обновлении, т.п.
// отключаем синтез речи
$(window).on('beforeunload', function() {
that.stop();
});
};
this.runGeneration = function() {
var i, synthConfig;
// сбросим состояние на всякий случай
this.stop();
// и не забудем проставить статус
this.status = this.STATES.RUN;
for(i in this.textParts) {
synthConfig = new SpeechSynthesisUtterance();
synthConfig.lang = 'ru-RU';
synthConfig.text = this.textParts[i];
that.engine.speak(synthConfig);
}
};
this.stop = function() {
this.status = this.STATES.STOP;
this.engine.cancel();
};
this.pause = function() {
this.status = this.STATES.PAUSE;
this.engine.pause();
};
this.resume = function() {
this.status = this.STATES.RUN;
this.engine.resume();
};
return this.init();
};
window.speak = new SpeechGenerator();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment