UNPKG

@huluvu424242/honey-speech

Version:

Text to Speech component wich is reading texts from DOM elements.

366 lines (358 loc) 12.6 kB
import { attachShadow, createEvent, h, Host, proxyCustomElement } from '@stencil/core/internal/client'; export { setAssetPath } from '@stencil/core/internal/client'; class Logger { constructor(enableLogging) { Logger.isLoggingActive = enableLogging; } static disableLogging() { this.isLoggingActive = false; } static enableLogging() { this.isLoggingActive = true; } static toggleLogging(enableLogging) { if (enableLogging) { Logger.enableLogging(); } else { Logger.disableLogging(); } } static logMessage(message) { if (console && this.isLoggingActive) { console.log(message); } } static debugMessage(message) { if (console && this.isLoggingActive) { console.debug(message); } } static errorMessage(message) { if (console && this.isLoggingActive) { console.error(message); } } static infoMessage(message) { if (console && this.isLoggingActive) { console.info(message); } } } Logger.isLoggingActive = true; class Synthese { constructor() { this.sprachSynthese = window.speechSynthesis; this.sprachSynthese.onvoiceschanged = () => { if (!this.voices || this.voices.length < 1) { this.voices = this.sprachSynthese.getVoices(); Logger.debugMessage("voices changed to: " + this.voices.join(",")); } else { Logger.debugMessage("voices alraedy initialized"); } }; Logger.debugMessage("call getVoices()"); this.sprachSynthese.getVoices(); } getVoices() { return this.voices; } cancel() { this.sprachSynthese.cancel(); } } class Sprachausgabe { constructor(onSpeakerStarted, onSpeakerFinished, onSpeakerPaused, onSpeakerResume, onSpeakerFailed, audioLang, audioPitch, audioRate, audioVolume, voiceName) { this.onSpeakerStarted = (ev) => onSpeakerStarted(ev); this.onSpeakerFinished = (ev) => onSpeakerFinished(ev); this.onSpeakerPaused = (ev) => onSpeakerPaused(ev); this.onSpeakerResume = (ev) => onSpeakerResume(ev); this.onSpeakerFailed = (ev) => onSpeakerFailed(ev); this.audioLang = audioLang; this.audioPitch = audioPitch; this.audioRate = audioRate; this.audioVolume = audioVolume; this.voiceName = voiceName; this.stimme = undefined; Logger.infoMessage("####constructor finished"); } getDefaultStimme() { var namedMatch; var langMatches = []; var langDefaultMatch; var defaultMatch; const voices = Sprachausgabe.synthese.getVoices(); Logger.infoMessage("Found voices:" + JSON.stringify(voices)); if (!voices) return null; for (var i = 0; i < voices.length; i++) { if (voices[i].name === this.voiceName || voices[i].lang === this.audioLang || voices[i].default) { Logger.debugMessage("voice matched:" + voices[i].name + voices[i].lang); if (voices[i].name === this.voiceName) { namedMatch = voices[i]; } if (voices[i].lang === this.audioLang && voices[i].default) { langDefaultMatch = voices[i]; } if (voices[i].lang === this.audioLang) { langMatches.push(voices[i]); } if (voices[i].default) { defaultMatch = voices[i]; } } } // Auswertung if (namedMatch) { return namedMatch; } if (langDefaultMatch) { return langDefaultMatch; } if (langMatches && langMatches.length > 0) { return langMatches[0]; } if (defaultMatch) { return defaultMatch; } return voices[0]; } erzeugeVorleser(text) { Logger.infoMessage("erzeugeVorleser started"); const vorleser = new SpeechSynthesisUtterance(text); vorleser.onend = this.onSpeakerFinished; vorleser.onstart = this.onSpeakerStarted; vorleser.onpause = this.onSpeakerPaused; vorleser.onresume = this.onSpeakerResume; vorleser.onerror = this.onSpeakerFailed; vorleser.pitch = this.audioPitch; vorleser.rate = this.audioRate; vorleser.volume = this.audioVolume; vorleser.voice = this.stimme; vorleser.lang = this.audioLang; return vorleser; } textVorlesen(zuLesenderText) { if (!this.stimme) { this.stimme = this.getDefaultStimme(); Logger.infoMessage("set default voice to " + this.stimme); } if (zuLesenderText) { const texte = zuLesenderText.match(/(\S+\s){1,20}/g); texte.forEach(text => { const vorleser = this.erzeugeVorleser(text); Logger.infoMessage("speaker lang used:" + vorleser.lang); if (vorleser.voice) { Logger.infoMessage("speaker voice used:" + vorleser.voice.name); Logger.infoMessage("speaker voice lang:" + vorleser.voice.lang); } else { Logger.infoMessage("no voice matched for text: " + zuLesenderText); } Sprachausgabe.synthese.sprachSynthese.speak(vorleser); }); } } cancel() { Sprachausgabe.synthese.sprachSynthese.cancel(); } } Sprachausgabe.synthese = new Synthese(); const honeySpeechCss = ".speakerimage{background:var(--honey-speaker-background, transparent);stroke:var(--honey-speaker-color, blue);fill:var(--honey-speaker-color, blue);padding:var(--honey-speaker-padding, 5px);font-size:var(--honey-speaker-font-size, medium);border:var(--honey-speaker-border, 0);width:var(--honey-speaker-width, 36px);height:var(--honey-speaker-height, 36px)}"; const HoneySpeech = class extends HTMLElement { constructor() { super(); this.__registerHost(); attachShadow(this); this.honeySpeakerStarted = createEvent(this, "honeySpeakerStarted", 7); this.honeySpeakerFinished = createEvent(this, "honeySpeakerFinished", 7); this.honeySpeakerPaused = createEvent(this, "honeySpeakerPaused", 7); this.honeySpeakerResume = createEvent(this, "honeySpeakerResume", 7); this.honeySpeakerFailed = createEvent(this, "honeySpeakerFailed", 7); /** * true wenn das Tag ohne alt Attribute deklariert wurde */ this.createAltText = false; /** * true wenn das Tag ohne title Attribut deklariert wurde */ this.createTitleText = false; /** * taborder */ this.taborder = "0"; /** * if the toggle button is pressed */ this.isPressed = false; /** * use pure speaker symbol for silence state */ this.pure = false; /** * enable console logging */ this.verbose = false; /** * icon width */ this.iconwidth = "36"; /** * icon height */ this.iconheight = "36"; /** * i18n language ident for Web Speech API: de-DE or en or de ... */ this.audiolang = "de-DE"; /** * pitch for Web Speech API */ this.audiopitch = 1; /** * rate for Web Speech API */ this.audiorate = 1; /** * volume for Web Speech API */ this.audiovolume = 1; /** * voice name used of Web Speech API */ this.voicename = undefined; } connectedCallback() { // States initialisieren this.ident = this.hostElement.id ? this.hostElement.id : Math.random().toString(36).substring(7); this.createTitleText = !this.hostElement.title; this.createAltText = !this.hostElement["alt"]; this.taborder = this.hostElement.getAttribute("tabindex") ? (this.hostElement.tabIndex + "") : "0"; // Properties auswerten Logger.toggleLogging(this.verbose); } componentWillLoad() { this.sprachAusgabe = new Sprachausgabe(() => { this.honeySpeakerStarted.emit(this.ident); this.isPressed = true; Logger.debugMessage("Vorlesen gestartet"); }, () => { this.honeySpeakerFinished.emit(this.ident); this.isPressed = false; Logger.debugMessage("Vorlesen beendet"); }, () => { this.honeySpeakerPaused.emit(this.ident); this.isPressed = false; Logger.debugMessage("Pause mit Vorlesen"); }, () => { this.honeySpeakerResume.emit(this.ident); this.isPressed = true; Logger.debugMessage("Fortsetzen mit Vorlesen"); }, (ev) => { this.honeySpeakerFailed.emit(this.ident); this.isPressed = false; Logger.errorMessage("Fehler beim Vorlesen" + JSON.stringify(ev)); }, this.audiolang, this.audiopitch, this.audiorate, this.audiovolume, this.voicename); } createNewTitleText() { if (this.isPressed) { return "Liest gerade vor"; } else { return "Vorlesen"; } } getTitleText() { if (this.createTitleText) { return this.createNewTitleText(); } else { return this.hostElement.title; } } createNewAltText() { if (this.isPressed) { return "Symbol eines stummen Lautsprechers"; } else { return "Symbol eines tönenden Lautsprechers"; } } getAltText() { if (this.createAltText) { return this.createNewAltText(); } else { return this.hostElement.getAttribute("alt"); } } getTexte() { if (this.textids) { const refIds = this.textids.split(","); const texte = []; refIds.forEach(elementId => { const element = document.getElementById(elementId); if (element) { texte.push(element.innerText); } else { Logger.errorMessage("text to speak not found of DOM element with id " + elementId); } }); return texte; } else { return ["Kein Text vorhanden, daher keine Ausgabe möglich."]; } } async textVorlesen(text) { this.isPressed = true; await this.sprachAusgabe.textVorlesen(text + " "); } toggleAction() { Logger.debugMessage("###TOGGLE TO" + this.isPressed); this.isPressed = !this.isPressed; if (this.isPressed) { const texte = this.getTexte(); texte.forEach(async (text) => { this.textVorlesen(text); }); } else { this.sprachAusgabe.cancel(); } } onClick() { this.toggleAction(); } onKeyDown(ev) { if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); this.toggleAction(); } } render() { Logger.debugMessage('##RENDER##'); return (h(Host, { title: this.getTitleText(), alt: this.getAltText(), role: "button", tabindex: this.taborder, "aria-pressed": this.isPressed ? "true" : "false" }, this.isPressed ? (h("svg", { id: this.ident + "-svg", xmlns: "http://www.w3.org/2000/svg", width: this.iconwidth, height: this.iconheight, role: "img", "aria-label": this.getAltText(), class: "speakerimage", viewBox: "0 0 75 75" }, h("path", { "stroke-width": "5", "stroke-linejoin": "round", d: "M39.389,13.769 L22.235,28.606 L6,28.606 L6,47.699 L21.989,47.699 L39.389,62.75 L39.389,13.769z" }), h("path", { id: "air", stroke: "var(--honey-speaker-color,black);", fill: "none", "stroke-width": "5", "stroke-linecap": "round", d: "M48,27.6a19.5,19.5 0 0 1 0,21.4M55.1,20.5a30,30 0 0 1 0,35.6M61.6,14a38.8,38.8 0 0 1 0,48.6" }, h("animate", { id: "airanimation", attributeType: "CSS", attributeName: "opacity", from: "1", to: "0", dur: "1s", repeatCount: "indefinite" })))) : (h("svg", { id: this.ident + "-svg", xmlns: "http://www.w3.org/2000/svg", width: this.iconwidth, height: this.iconheight, role: "img", "aria-label": this.getAltText(), class: "speakerimage", viewBox: "0 0 75 75" }, h("path", { "stroke-width": "5", "stroke-linejoin": "round", d: "M39.389,13.769 L22.235,28.606 L6,28.606 L6,47.699 L21.989,47.699 L39.389,62.75 L39.389,13.769z" }), this.pure ? (h("text", { id: "eins", x: "60%", y: "55%" }, "OFF")) : (h("path", { id: "air", stroke: "var(--honey-speaker-color,black);", fill: "none", "stroke-width": "5", "stroke-linecap": "round", d: "M48,27.6a19.5,19.5 0 0 1 0,21.4M55.1,20.5a30,30 0 0 1 0,35.6M61.6,14a38.8,38.8 0 0 1 0,48.6" })))))); } static get assetsDirs() { return ["assets"]; } get hostElement() { return this; } static get style() { return honeySpeechCss; } }; const HoneySpeech$1 = /*@__PURE__*/proxyCustomElement(HoneySpeech, [1,"honey-speech",{"pure":[4],"textids":[1],"verbose":[4],"iconwidth":[1],"iconheight":[1],"audiolang":[1],"audiopitch":[2],"audiorate":[2],"audiovolume":[2],"voicename":[1],"isPressed":[32]},[[2,"click","onClick"],[2,"keydown","onKeyDown"]]]); const defineCustomElements = (opts) => { if (typeof customElements !== 'undefined') { [ HoneySpeech$1 ].forEach(cmp => { if (!customElements.get(cmp.is)) { customElements.define(cmp.is, cmp, opts); } }); } }; export { HoneySpeech$1 as HoneySpeech, defineCustomElements };