import { Inject } from "@nuxt/types/app"; import { AudioConfig, IPlayer, ResultReason, SpeakerAudioDestination, SpeechConfig, SpeechSynthesisBookmarkEventArgs, SpeechSynthesisEventArgs, SpeechSynthesisResult, SpeechSynthesisVisemeEventArgs, SpeechSynthesisWordBoundaryEventArgs, SpeechSynthesizer, } from "microsoft-cognitiveservices-speech-sdk"; import { Events } from "~/enums/events"; import { Component } from "nuxt-property-decorator"; import { BehaviorSubject } from "rxjs"; import VueBase from "~/myclasses/VueBase"; import { VueContext } from "~/types/vue-context"; import { Nullable } from "~/types/mytypes"; // https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-services-quotas-and-limits const SPEECH_KEY = "2b4fffa8572346fca1f357f5de0e68d8"; const SPEECH_REGION = "eastus"; /** Declaration */ declare module "vue/types/vue" { interface Vue { $ttsMgr: TTSManager; } } /** Component */ @Component export class TTSManager extends VueBase { m_speechConfig: SpeechConfig | null = null; m_player: SpeakerAudioDestination | null = null; m_synthesizer: SpeechSynthesizer | null = null; // private static sm_instance: TTSManager; created() { this.log("TTSManager"); } initializePlayer() { try { this.log("initializePlayer"); if (this.m_player) { this.m_player.pause(); this.m_player.close(); this.m_player = null; } this.wordBoundaryList = []; this.m_player = new SpeakerAudioDestination(); this.m_player.onAudioStart = this.onAudioStart; this.m_player.onAudioEnd = this.onAudioEnd; const audioConfig = AudioConfig.fromSpeakerOutput(this.m_player!); this.m_speechConfig = SpeechConfig.fromSubscription( SPEECH_KEY, SPEECH_REGION ); // const aria = "en-US-AriaNeural"; const sara = "en-US-SaraNeural"; // const abbi = "en-GB-AbbiNeural" // Set either the `SpeechSynthesisVoiceName` or `SpeechSynthesisLanguage`. this.m_speechConfig.speechSynthesisLanguage = "en-US"; this.m_speechConfig.speechSynthesisVoiceName = sara; this.m_synthesizer = new SpeechSynthesizer( this.m_speechConfig!, audioConfig ); } catch (err) { this.error(err); } } pause() { this.log("pause"); this.m_player?.pause(); } resume() { this.log("resume"); this.m_player?.resume(); } stop() { try { this.log("stop"); if (this.audioPlaying){ this.pause(); } if (this.m_player) this.m_player?.close(); this.m_player = null; if (this.speechSynthesis){ this.speechSynthesis.cancel(); this.speechSynthesis = null; } this.audioPlaying = false; } catch (error) { this.error(error); } } player$ = new BehaviorSubject(this.m_player); wordBoundaryList$ = new BehaviorSubject< SpeechSynthesisWordBoundaryEventArgs[] >(this.wordBoundaryList); audioPlaying$ = new BehaviorSubject(false); m_audioPlaying = false; get audioPlaying(): boolean { return this.m_audioPlaying; } get player() { return this.m_player; } set audioPlaying(value: boolean) { this.m_audioPlaying = value; this.audioPlaying$.next(value); } onAudioStart(sender: IPlayer) { this.log("onAudioStart"); this.audioPlaying = true; // this.log("currentTime", sender.currentTime); // this.$root.$emit(Events.AUDIO_PLAYING, true) } onAudioEnd(sender: IPlayer) { this.log("onAudioEnd"); this.log(sender.currentTime); this.audioPlaying = false; this.m_player = null; // this.wordBoundaryList = []; // this.$root.$emit(Events.AUDIO_PLAYING, false) } onBookmarkReached( _sender: SpeechSynthesizer, event: SpeechSynthesisBookmarkEventArgs ) { this.log("onBookmarkReached", event); } onSynthesisCompleted( _sender: SpeechSynthesizer, _event: SpeechSynthesisEventArgs ) { this.log("onSynthesisCompleted"); this.$root.$emit(Events.TEXT_SYNTHESIZED, this.textToSpeak); } onSynthesisStarted( _sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs ) { this.log("onSynthesisStarted", event); } onSynthesisCanceled( _sender: SpeechSynthesizer, _event: SpeechSynthesisEventArgs ) { this.log("onSynthesisCanceled"); } onSynthesizing(_sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs) { // this.log('onSynthesizing', event) } onVisemeReceived( _sender: SpeechSynthesizer, _event: SpeechSynthesisVisemeEventArgs ) { this.log("onVisemeReceived"); } m_wordBoundaryList: SpeechSynthesisWordBoundaryEventArgs[] = []; get wordBoundaryList(): SpeechSynthesisWordBoundaryEventArgs[] { return this.m_wordBoundaryList; } set wordBoundaryList(value: SpeechSynthesisWordBoundaryEventArgs[]) { this.m_wordBoundaryList = value; this.wordBoundaryList$.next(this.wordBoundaryList); } onWordBoundary( _sender: SpeechSynthesizer, event: SpeechSynthesisWordBoundaryEventArgs ) { // this.log("onWordBoundary"); this.wordBoundaryList.push(event); // this.log("onWordBoundary", this.wordBoundaryList); // this.wordBoundaryList$.next(this.wordBoundaryList); } textToSpeak$ = new BehaviorSubject(this.textToSpeak); m_textToSpeak = ""; get textToSpeak(): string { return this.m_textToSpeak; } set textToSpeak(value: string) { this.m_textToSpeak = value; this.textToSpeak$.next(value); } m_useVoice = true; get useVoice(): boolean { return this.m_useVoice; } set useVoice(value: boolean) { this.m_useVoice = value; } onResult(result: SpeechSynthesisResult) { try { this.m_synthesizer!.close(); this.m_synthesizer = null; if (result.reason == ResultReason.SynthesizingAudioCompleted) { this.log("synthesis complete"); return result.audioData; } this.log("error", result.errorDetails); this.audioPlaying = false; } catch (err) { this.error(err); } } onError(error: string) { try { this.log("onError", error); this.m_synthesizer?.close(); this.m_synthesizer = null; } catch (err) { this.error(err); } } // async synthesizeSpeechHighlight(_textToSpeak: string): Promise {} setupListeners() { this.m_synthesizer!.bookmarkReached = ( sender: SpeechSynthesizer, event: SpeechSynthesisBookmarkEventArgs ) => this.onBookmarkReached(sender, event); this.m_synthesizer!.SynthesisCanceled = ( sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs ) => this.onSynthesisCanceled(sender, event); this.m_synthesizer!.synthesisCompleted = ( sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs ) => this.onSynthesisCompleted(sender, event); this.m_synthesizer!.synthesisStarted = ( sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs ) => this.onSynthesisStarted(sender, event); this.m_synthesizer!.synthesizing = ( sender: SpeechSynthesizer, event: SpeechSynthesisEventArgs ) => this.onSynthesizing(sender, event); this.m_synthesizer!.visemeReceived = ( sender: SpeechSynthesizer, event: SpeechSynthesisVisemeEventArgs ) => this.onVisemeReceived(sender, event); this.m_synthesizer!.wordBoundary = ( sender: SpeechSynthesizer, event: SpeechSynthesisWordBoundaryEventArgs ) => this.onWordBoundary(sender, event); } speechSynthesis:any = null; synthesizeSpeechWeb(textToSpeak: string) { try { if (!("speechSynthesis" in window)) { this.log("Text to speech not supported"); return; } // Regex to match all English language tags e.g en, en-US, en-GB var langRegex = /^en(-[a-z]{2})?$/i; this.speechSynthesis = window.speechSynthesis; // Get the available voices and filter the list to only have English speakers var voices = this.speechSynthesis .getVoices() .filter((voice) => langRegex.test(voice.lang)); // Log the properties of the voices in the list voices.forEach(function (voice) { console.log({ name: voice.name, lang: voice.lang, uri: voice.voiceURI, local: voice.localService, default: voice.default, }); }); // Get the first `en` language voice in the list var voice = this.speechSynthesis.getVoices().filter(function (voice) { return voice.lang === "en"; })[0]; // Create an utterance object var utterance = new SpeechSynthesisUtterance(textToSpeak); // Set utterance properties utterance.voice = voice; utterance.pitch = 1.0; utterance.rate = 1.0; utterance.volume = 0.8; // Speak the utterance this.audioPlaying = true; this.textToSpeak = textToSpeak; this.speechSynthesis.speak(utterance); const interval = window.setInterval((event:any) => { if (!this.speechSynthesis.speaking){ this.audioPlaying = false; window.clearInterval(interval); } }, 5*1000); } catch (err) { this.error(err); } }/* play.ht user id 8bm0V7tf1cQ1tHmOvSzHhyfFnZC2 secret 686aa48ba6ac411baa130d05352ec542 */ synthesizeSpeech(textToSpeak: string): void { this.synthesizeSpeechWeb(textToSpeak); } synthesizeSpeechMs(textToSpeak: string): void { try { if (!this.useVoice) return; this.log("synthesizeSpeech", textToSpeak); this.initializePlayer(); this.setupListeners(); this.textToSpeak = textToSpeak; this.wordBoundaryList = []; this.m_synthesizer!.speakTextAsync( textToSpeak, this.onResult, this.onError ); } catch (err) { this.error(err); } } } /** Injection */ export default function (_context: VueContext, inject: Inject) { inject("ttsMgr", new TTSManager()); }