Да да, синтез речи на форуме. То есть озвучиваем текстовые сообщения на форуме по средствам встроенных возможностей браузера (Web Speech API)
Вот скрипт:
Для установки скрипта на firefox я использовал расширение
Как-то так это все безобразие выглядит
Посмотреть вложение Новый проект2.mp4
Посмотреть вложение Новый проект.mp4
Вся необходимая информация по Web Speech API была взята с этого сайта
Вот скрипт:
JavaScript:
const url = window.location.href
if (url.startsWith('https://mmo-dev.info/threads/')) {
window.addEventListener("load", () => {
class TTS {
#_speechSynthesis = window.speechSynthesis || null;
#_speechSynthesisUtterance = new SpeechSynthesisUtterance();
voiceId = -1;
constructor() {
this.#_speechSynthesisUtterance.volume = localStorage.getItem('tts-volume') || 1;
this.#_speechSynthesisUtterance.pitch = localStorage.getItem('tts-pitch') || 1;
this.#_speechSynthesisUtterance.rate = localStorage.getItem('tts-rate') || 1;
}
loadVoicesAsync() {
return new Promise((resolve, reject) => {
if (this.#_speechSynthesis) {
let timerId, attempts = 0;
timerId = setInterval(() => {
if (++attempts > 20) {
reject();
clearInterval(timerId);
} else {
if (this.#_speechSynthesis.getVoices().length !== 0) {
resolve();
clearInterval(timerId);
}
}
}, 100);
} else {
reject('SpeechSynthesis is not supported!');
}
});
}
getSpeechSynthesis() {
return this.#_speechSynthesis;
}
getSpeechSynthesisUtterance() {
return this.#_speechSynthesisUtterance;
}
setVolume(val) {
localStorage.setItem('tts-volume', this.#_speechSynthesisUtterance.volume = val);
}
setPitch(val) {
localStorage.setItem('tts-pitch', this.#_speechSynthesisUtterance.pitch = val);
}
setRate(val) {
localStorage.setItem('tts-rate', this.#_speechSynthesisUtterance.rate = val);
}
setVoice(val) {
this.#_speechSynthesisUtterance.voice = val;
}
setLang(val) {
this.#_speechSynthesisUtterance.lang = val;
}
}
const tts = new TTS();
tts.loadVoicesAsync().then(() => {
const ss = tts.getSpeechSynthesis();
ss.cancel();
//ss.onvoiceschanged = () => {};
const ssu = tts.getSpeechSynthesisUtterance();
const voices = ss.getVoices()/*.filter(v => {
return v.lang == document.documentElement.lang
})*/;
if (voices.length > 0) {
tts.setVoice(voices[0]);
tts.setLang(voices[0].lang);
}
try {
let messages = document.querySelectorAll('article.message-body.js-selectToQuote > div > div.bbWrapper');
messages.forEach((message, index) => {
let panel = document.createElement('div');
panel.className = 'tts-panel';
panel.style = 'width: 500px; margin-left: auto;';
// Voice ----------------------
let fieldsetVoice = document.createElement('fieldset');
let legendVoice = document.createElement('legend');
legendVoice.innerText = 'Voice';
fieldsetVoice.appendChild(legendVoice);
let selectVoice = document.createElement('select');
selectVoice.id = selectVoice.name = 'tts-voice-' + index;
selectVoice.className = 'tts-voice';
voices.forEach(voice => {
const option = document.createElement("option");
option.value = voice.voiceURI;
option.innerText = voice.name;
selectVoice.appendChild(option);
});
selectVoice.onchange = (e) => {
voices.forEach(voice => {
const value = e.target.options[e.target.selectedIndex].value;
if (voice.voiceURI === value) {
tts.setVoice(voice);
tts.setLang(voice.lang);
}
});
};
fieldsetVoice.appendChild(selectVoice);
let btnStop = document.createElement('button');
btnStop.id = 'tts-stop-' + index;
btnStop.innerHTML = 'stop';
btnStop.disabled = true;
btnStop.onclick = (e) => {
btnStop.disabled = true;
ss.cancel();
};
let btnPlayResume = document.createElement('button');
btnPlayResume.style = 'margin: 0 5px;';
btnPlayResume.id = 'tts-play-pause-' + index;
btnPlayResume.innerText = 'play';
btnPlayResume.onclick = (e) => {
if (tts.voiceId !== index) {
ss.cancel();
}
setTimeout(() => {
//console.info('ss', ss);
//console.info('ssu', ssu);
/*if (ss.pending) {
return;
}*/
if (ss.speaking && ss.paused) {
ss.resume();
} else if (ss.speaking) {
ss.pause();
} else {
tts.voiceId = index;
ssu.text = message.innerText.trim().replace(/\s+/g, ' ');
//ssu.onboundary = () => {};
//ssu.onmark = () => {};
ssu.onstart = (e) => {
btnStop.disabled = false;
btnPlayResume.innerText = 'pause';
};
ssu.onpause = (e) => {
btnPlayResume.innerText = 'resume';
};
ssu.onresume = (e) => {
btnPlayResume.innerText = 'pause';
};
ssu.onend = (e) => {
btnStop.disabled = true;
btnPlayResume.innerText = 'play';
};
ssu.onerror = (e) => {
console.info('SpeechSynthesisUtterance', e)
}
ss.speak(ssu);
}
}, 100);
}
fieldsetVoice.appendChild(btnPlayResume);
fieldsetVoice.appendChild(btnStop);
panel.appendChild(fieldsetVoice);
// ------------------------
// Volume ------------------
let fieldsetVolume = document.createElement('fieldset');
let legendVolume = document.createElement('legend');
legendVolume.innerText = 'Volume (' + parseInt( ssu.volume * 100 ) + '%)';
fieldsetVolume.appendChild(legendVolume);
let inputVolume = document.createElement('input');
inputVolume.id = inputVolume.name = 'tts-volume-' + index;
inputVolume.className = 'tts-volume';
inputVolume.style = 'width:100%';
inputVolume.type = 'range';
inputVolume.min = 0;
inputVolume.max = 1;
inputVolume.step = 0.1;
inputVolume.value = ssu.volume;
inputVolume.onchange = (e) => {
legendVolume.innerText = 'Volume (' + parseInt( e.target.value * 100 ) + '%)';
tts.setVolume(e.target.value);
};
fieldsetVolume.appendChild(inputVolume);
panel.appendChild(fieldsetVolume);
// ----------------
// Pitch ------------------
let fieldsetPitch = document.createElement('fieldset');
let legendPitch = document.createElement('legend');
legendPitch.innerText = 'Pitch (' + ssu.pitch + ')';
fieldsetPitch.appendChild(legendPitch);
let inputPitch = document.createElement('input');
inputPitch.id = inputPitch.name = 'tts-pitch-' + index;
inputPitch.className = 'tts-pitch';
inputPitch.style = 'width:100%';
inputPitch.type = 'range';
inputPitch.min = 0;
inputPitch.max = 2;
inputPitch.step = 0.5;
inputPitch.value = ssu.pitch;
inputPitch.onchange = (e) => {
legendPitch.innerText = 'Pitch (' + e.target.value + ')';
tts.setPitch(e.target.value);
};
fieldsetPitch.appendChild(inputPitch);
panel.appendChild(fieldsetPitch);
// ----------------
// Rate ------------------
let fieldsetRate = document.createElement('fieldset');
let legendRate = document.createElement('legend');
legendRate.innerText = 'Rate (' + ssu.rate + ')';
fieldsetRate.appendChild(legendRate);
let inputRate = document.createElement('input');
inputRate.id = inputRate.name = 'tts-rate-' + index;
inputRate.className = 'tts-rate';
inputRate.style = 'width:100%';
inputRate.type = 'range';
inputRate.min = 0;
inputRate.max = 10;
inputRate.step = 1;
inputRate.value = ssu.rate;
inputRate.onchange = (e) => {
legendRate.innerText = 'Rate (' + e.target.value + ')';
tts.setRate(e.target.value);
};
fieldsetRate.appendChild(inputRate);
panel.appendChild(fieldsetRate);
// ---------------------
message.before(panel);
})
} catch (e) {
console.info(e)
}
}).catch((e) => {
console.info('TTS:', e)
})
});
}
Для установки скрипта на firefox я использовал расширение
Вы не можете просматривать ссылку пожалуйста воспользуйтесь следующими ссылками
Вход или Регистрация
. После установки расширения достаточно в нем выбрать "Создать пользовательский скрипт", добавить наш код и сохранить.Как-то так это все безобразие выглядит
Посмотреть вложение Новый проект2.mp4
Посмотреть вложение Новый проект.mp4
Вся необходимая информация по Web Speech API была взята с этого сайта
Вы не можете просматривать ссылку пожалуйста воспользуйтесь следующими ссылками
Вход или Регистрация
Последнее редактирование: