-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
TTS: Support Web Speech API (almost complete PR) #661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { create } from 'zustand'; | ||
| import { persist } from 'zustand/middleware'; | ||
| import { useShallow } from 'zustand/react/shallow'; | ||
| import { ASREngineList, TTSEngineList } from '~/common/components/useVoiceCapabilities'; | ||
|
|
||
| import type { DLLMId } from '~/common/stores/llms/llms.types'; | ||
|
|
||
|
|
@@ -51,6 +52,12 @@ interface AppChatStore { | |
| micTimeoutMs: number; | ||
| setMicTimeoutMs: (micTimeoutMs: number) => void; | ||
|
|
||
| TTSEngine: string; | ||
|
||
| setTTSEngine: (TTSEngine: string) => void; | ||
|
|
||
| ASREngine: string; | ||
| setASREngine: (ASREngine: string) => void; | ||
|
|
||
| showPersonaIcons: boolean; | ||
| setShowPersonaIcons: (showPersonaIcons: boolean) => void; | ||
|
|
||
|
|
@@ -114,6 +121,12 @@ const useAppChatStore = create<AppChatStore>()(persist( | |
| micTimeoutMs: 2000, | ||
| setMicTimeoutMs: (micTimeoutMs: number) => _set({ micTimeoutMs }), | ||
|
|
||
| TTSEngine: TTSEngineList[0], | ||
|
||
| setTTSEngine: (TTSEngine: string) => _set({ TTSEngine }), | ||
|
|
||
| ASREngine: ASREngineList[0], | ||
|
||
| setASREngine: (ASREngine: string) => _set({ ASREngine }), | ||
|
|
||
| showPersonaIcons: true, | ||
| setShowPersonaIcons: (showPersonaIcons: boolean) => _set({ showPersonaIcons }), | ||
|
|
||
|
|
@@ -198,6 +211,13 @@ export const useChatMicTimeoutMsValue = (): number => | |
| export const useChatMicTimeoutMs = (): [number, (micTimeoutMs: number) => void] => | ||
| useAppChatStore(useShallow(state => [state.micTimeoutMs, state.setMicTimeoutMs])); | ||
|
|
||
| export const useTTSEngine = (): [string, (micTimeoutMs: string) => void] => | ||
| useAppChatStore(useShallow(state => [state.TTSEngine, state.setTTSEngine])); | ||
| export const getTTSEngine = () => useAppChatStore.getState().TTSEngine; | ||
|
|
||
| export const useASREngine = (): [string, (micTimeoutMs: string) => void] => | ||
| useAppChatStore(useShallow(state => [state.ASREngine, state.setASREngine])); | ||
|
|
||
| export const useChatDrawerFilters = () => { | ||
| const values = useAppChatStore(useShallow(state => ({ | ||
| filterHasDocFragments: state.filterHasDocFragments, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| import { getTTSEngine } from 'src/apps/chat/store-app-chat'; | ||
| import { CapabilitySpeechSynthesis } from '~/common/components/useCapabilities'; | ||
|
|
||
| import { useCapability as useElevenlabsCapability } from '~/modules/elevenlabs/elevenlabs.client' | ||
| import { speakText as elevenlabsSpeakText } from '~/modules/elevenlabs/elevenlabs.client' | ||
| import { EXPERIMENTAL_speakTextStream as EXPERIMENTAL_elevenlabsSpeakTextStream } from '~/modules/elevenlabs/elevenlabs.client' | ||
|
|
||
| import { useCapability as useBrowserSpeechSynthesisCapability } from '~/modules/browser/speech-synthesis/browser.speechSynthesis.client' | ||
| import { speakText as browserSpeechSynthesisSpeakText } from '~/modules/browser/speech-synthesis/browser.speechSynthesis.client' | ||
| import { EXPERIMENTAL_speakTextStream as EXPERIMENTAL_browserSpeechSynthesisSpeakTextStream } from '~/modules/browser/speech-synthesis/browser.speechSynthesis.client' | ||
|
|
||
| import { useElevenLabsVoices } from '~/modules/elevenlabs/useElevenLabsVoiceDropdown'; | ||
| import { useBrowserSpeechVoices } from '~/modules/browser/speech-synthesis/useBrowserSpeechVoiceDropdown'; | ||
|
|
||
| export const TTSEngineList: string[] = [ | ||
|
||
| 'Elevenlabs', | ||
| 'Web Speech API' | ||
| ] | ||
|
|
||
| export const ASREngineList: string[] = [ | ||
| 'Web Speech API' | ||
| ] | ||
|
|
||
| export function getConditionalVoices(){ | ||
| const TTSEngine = getTTSEngine(); | ||
| if (TTSEngine === 'Elevenlabs') { | ||
| return useElevenLabsVoices | ||
| }else if (TTSEngine === 'Web Speech API') { | ||
| return useBrowserSpeechVoices | ||
| } | ||
| throw new Error('TTSEngine is not found'); | ||
| } | ||
|
|
||
| export function hasVoices(): boolean { | ||
| console.log('getConditionalVoices', getConditionalVoices()().hasVoices) | ||
| return getConditionalVoices()().hasVoices; | ||
| } | ||
|
|
||
| export function getConditionalCapability(): () => CapabilitySpeechSynthesis { | ||
| const TTSEngine = getTTSEngine(); | ||
| if (TTSEngine === 'Elevenlabs') { | ||
| return useElevenlabsCapability | ||
| }else if (TTSEngine === 'Web Speech API') { | ||
| return useBrowserSpeechSynthesisCapability | ||
| } | ||
| throw new Error('TTSEngine is not found'); | ||
| } | ||
|
|
||
| export function useCapability(): CapabilitySpeechSynthesis { | ||
|
||
| return getConditionalCapability()(); | ||
| } | ||
|
|
||
|
|
||
| export async function speakText(text: string, voiceId?: string) { | ||
| const TTSEngine = getTTSEngine(); | ||
| if (TTSEngine === 'Elevenlabs') { | ||
| return await elevenlabsSpeakText(text, voiceId); | ||
|
||
| }else if (TTSEngine === 'Web Speech API') { | ||
| return await browserSpeechSynthesisSpeakText(text, voiceId); | ||
| } | ||
| throw new Error('TTSEngine is not found'); | ||
| } | ||
|
|
||
| // let liveAudioPlayer: LiveAudioPlayer | undefined = undefined; | ||
|
|
||
| export async function EXPERIMENTAL_speakTextStream(text: string, voiceId?: string) { | ||
| const TTSEngine = getTTSEngine(); | ||
| if (TTSEngine === 'Elevenlabs') { | ||
| return await EXPERIMENTAL_elevenlabsSpeakTextStream(text, voiceId); | ||
| }else if (TTSEngine === 'Web Speech API') { | ||
| return await EXPERIMENTAL_browserSpeechSynthesisSpeakTextStream(text, voiceId); | ||
| } | ||
| throw new Error('TTSEngine is not found'); | ||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love this.