Spaces:
Paused
Paused
| "use client" | |
| import { getSpeechSynthesisVoice } from "@/lib/getSpeechSynthesisVoice" | |
| import { create } from "zustand" | |
| export const useStore = create<{ | |
| isSpeechSynthesisAvailable: boolean | |
| speechSynthesis: SpeechSynthesis | |
| speechSynthesisVoice: SpeechSynthesisVoice | |
| isSpeaking: boolean | |
| lastSpokenSentence: string | |
| isHearing: boolean // robot is hearing | |
| init: () => void, | |
| loadVoice: () => void, | |
| speak: (sentence: string) => void | |
| setHearing: (isHearing: boolean) => void | |
| }>((set, get) => ({ | |
| isSpeechSynthesisAvailable: false, | |
| speechSynthesis: undefined as unknown as SpeechSynthesis, | |
| speechSynthesisVoice: undefined as unknown as SpeechSynthesisVoice, | |
| isSpeaking: false, | |
| lastSpokenSentence: "", | |
| isHearing: false, // robot is taking | |
| init: () => { | |
| if (!window?.speechSynthesis) { | |
| console.error(`no speech synthesis engine available`) | |
| return | |
| } | |
| const speechSynthesis = window.speechSynthesis | |
| set({ speechSynthesis }) | |
| speechSynthesis.onvoiceschanged = () => { get().loadVoice() } | |
| setTimeout(() => { | |
| get().loadVoice() | |
| }, 2000) | |
| // due to the lack of event for the speaking state, we create our own polling system | |
| // see https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis/speaking | |
| setInterval(() => { | |
| const { isSpeaking } = get() | |
| if (!speechSynthesis.speaking && isSpeaking) { | |
| set({ isSpeaking: false }) | |
| } else if (speechSynthesis.speaking && !isSpeaking) { | |
| set({ isSpeaking: true }) | |
| } | |
| }, 100) | |
| }, | |
| loadVoice: () => { | |
| let { speechSynthesis, speechSynthesisVoice } = get() | |
| if (!speechSynthesis) { | |
| console.error(`no speech synthesis engine available`) | |
| return | |
| } | |
| try { | |
| speechSynthesisVoice = getSpeechSynthesisVoice(speechSynthesis) | |
| if (!speechSynthesisVoice?.name) { | |
| throw new Error("no name for the voice") | |
| } | |
| } catch (err) { | |
| console.error(`no speech synthesis voice available: ${err}`) | |
| return | |
| } | |
| if (speechSynthesisVoice) { | |
| set({ speechSynthesisVoice, isSpeechSynthesisAvailable: true }) | |
| } | |
| }, | |
| speak: (sentence: string) => { | |
| const { speechSynthesis, speechSynthesisVoice } = get() | |
| if (!speechSynthesis || !speechSynthesisVoice) { return } | |
| speechSynthesis.cancel() | |
| const utterance = new SpeechSynthesisUtterance(sentence) | |
| utterance.voice = speechSynthesisVoice | |
| speechSynthesis.speak(utterance) | |
| set({ lastSpokenSentence: sentence }) | |
| }, | |
| setHearing: (isHearing: boolean) => { set({ isHearing }) }, | |
| })) | |