From 630b58604057d832696fe3cfe4b58ac02da14559 Mon Sep 17 00:00:00 2001 From: canisminor1990 Date: Tue, 7 Nov 2023 20:57:25 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20new=20section=20to=20?= =?UTF-8?q?README.md=20and=20create=20new=20API=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add a new section to the README.md file - Create a new file in the API directory - Implement functions and interfaces for fetching and processing speech data from various APIs - Modify functions related to speech synthesis - Update the utterance object based on provided options - Handle dependencies for the useEffect hook - Update the voiceList state when the voices change These changes introduce new features and improve the functionality of the code for speech data processing and synthesis. --- README.md | 16 ++++++++++ api/index.ts | 19 ------------ api/microsoft-speech.ts | 31 +++++++++++++++++++ lib/fetchMicrosoftSpeech.ts | 42 -------------------------- lib/genSSML.ts | 35 ---------------------- src/const/api.ts | 7 ++++- src/index.ts | 10 +++---- src/services/fetchAzureSpeech.ts | 3 +- src/services/fetchEdgeSpeech.ts | 22 ++++++++------ src/services/fetchMicrosoftSpeech.ts | 45 ++++++++++++++++++++++------ src/services/fetchOpenaiSTT.ts | 4 +-- src/services/fetchOpenaiTTS.ts | 5 ++-- src/useAzureSpeech/index.ts | 11 ++++--- src/useEdgeSpeech/demos/index.tsx | 21 ++++++++++++- src/useEdgeSpeech/index.ts | 9 +++--- src/useMicrosoftSpeech/index.ts | 14 ++++----- src/useOpenaiTTS/demos/index.tsx | 30 +------------------ src/useOpenaiTTS/index.ts | 8 ++--- src/useSpeechSynthes/index.ts | 12 ++++---- 19 files changed, 161 insertions(+), 183 deletions(-) delete mode 100644 api/index.ts create mode 100644 api/microsoft-speech.ts delete mode 100644 lib/fetchMicrosoftSpeech.ts delete mode 100644 lib/genSSML.ts diff --git a/README.md b/README.md index 7b341fe..e3b7c59 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ A high-quality & reliable TTS React Hooks library - [Compile with Next.js](#compile-with-nextjs) - [🛳 Self Hosting](#-self-hosting) - [Deploy to Vercel](#deploy-to-vercel) + - [Environment Variable](#environment-variable) - [⌨️ Local Development](#️-local-development) - [🤝 Contributing](#-contributing) - [🔗 More Products](#-more-products) @@ -81,6 +82,21 @@ Click button below to deploy your private plugins' gateway. [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Flobehub%2Flobe-tts&project-name=lobe-tts&repository-name=lobe-tts) +### Environment Variable + +This project provides some additional configuration items set with environment variables: + +| Environment Variable | Description | Example | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| `OPENAI_API_KEY` | This is the API key you apply on the OpenAI account page | `sk-xxxxxx...xxxxxx` | +| `OPENAI_PROXY_URL` | If you manually configure the OpenAI interface proxy, you can use this configuration item to override the default OpenAI API request base URL | `https://api.chatanywhere.cn/v1`
The default value is
`https://api.openai.com/v1` | +| `AZURE_SPEECH_KEY` | This is the API key of Azure Speech Service | | +| `AZURE_SPEECH_REGION` | This is the region of Azure Speech Service | | +| `MICROSOFT_SPEECH_PROXY_URL` | If you manually configure the Microsoft Speech interface proxy, you can use this configuration item to override the default Speech API request base URL | | +| `MICROSOFT_SPEECH_ALLOW_ORIGINS` | Allow origins , string or string array | | +| `EDDGE_API_TOKEN` | This is the API key of Edge Speech Service | `6A5AA1D4EAFF4E9FB37E23D68491D6F4` | +| `EDDGE_PROXY_URL` | If you manually configure the Edge interface proxy, you can use this configuration item to override the default Edge wss request base URL | `wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1` | +
[![][back-to-top]](#readme-top) diff --git a/api/index.ts b/api/index.ts deleted file mode 100644 index 10b3c8f..0000000 --- a/api/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import qs from 'query-string'; - -import cors from '../lib/cors'; -import { fetchMicrosoftSpeech } from '../lib/fetchMicrosoftSpeech'; -import { SsmlOptions } from '../lib/genSSML'; - -export const config = { - runtime: 'edge', -}; - -export default async (req: Request) => { - const { text, ...options }: SsmlOptions & { text: string } = qs.parseUrl(req.url).query as any; - - const res = await fetchMicrosoftSpeech(text, options); - - const newResponse = new Response(res.body, res); - - return cors(req, newResponse); -}; diff --git a/api/microsoft-speech.ts b/api/microsoft-speech.ts new file mode 100644 index 0000000..b2588df --- /dev/null +++ b/api/microsoft-speech.ts @@ -0,0 +1,31 @@ +import cors from '../lib/cors'; + +export const config = { + runtime: 'edge', +}; + +const API = + 'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak'; + +const MICROSOFT_SPEECH_ALLOW_ORIGINS = + process.env?.MICROSOFT_SPEECH_ALLOW_ORIGINS?.split(',') || undefined; + +export default async (req: Request) => { + if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 }); + + let origin = '*'; + + if (MICROSOFT_SPEECH_ALLOW_ORIGINS) { + const reqOrigin = req.headers.get('origin'); + if (reqOrigin && MICROSOFT_SPEECH_ALLOW_ORIGINS.includes(reqOrigin)) { + origin = reqOrigin; + } else { + return new Response('Origin Not Allowed', { status: 403 }); + } + } + + const res = await fetch(API, { body: req.body, headers: req.headers, method: 'POST' }); + const newResponse = new Response(res.body, res); + + return cors(req, newResponse, { methods: ['POST'], origin }); +}; diff --git a/lib/fetchMicrosoftSpeech.ts b/lib/fetchMicrosoftSpeech.ts deleted file mode 100644 index 63a2d5c..0000000 --- a/lib/fetchMicrosoftSpeech.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -import { type SsmlOptions, genSSML } from './genSSML'; - -const API = - 'https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak'; - -export const fetchMicrosoftSpeech = async (text: string, options: SsmlOptions) => { - const data = JSON.stringify({ - offsetInPlainText: 0, - properties: { - SpeakTriggerSource: 'AccTuningPagePlayButton', - }, - ssml: genSSML(text, options), - ttsAudioFormat: 'audio-24khz-160kbitrate-mono-mp3', - }); - - const DEFAULT_HEADERS = new Headers({ - 'accept': '*/*', - 'accept-language': 'zh-CN,zh;q=0.9', - 'authority': 'southeastasia.api.speech.microsoft.com', - 'content-type': 'application/json', - 'customvoiceconnectionid': uuidv4(), - 'origin': 'https://speech.microsoft.com', - 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', - 'sec-ch-ua-mobile': '?0', - 'sec-ch-ua-platform': '"Windows"', - 'sec-fetch-dest': 'empty', - 'sec-fetch-mode': 'cors', - 'sec-fetch-site': 'same-site', - 'user-agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36', - }); - - return await fetch(API, { - body: data, - headers: DEFAULT_HEADERS, - method: 'POST', - // @ts-ignore - responseType: 'arraybuffer', - }); -}; diff --git a/lib/genSSML.ts b/lib/genSSML.ts deleted file mode 100644 index 9558e77..0000000 --- a/lib/genSSML.ts +++ /dev/null @@ -1,35 +0,0 @@ -export type StyleName = - | 'affectionate' - | 'angry' - | 'calm' - | 'cheerful' - | 'disgruntled' - | 'embarrassed' - | 'fearful' - | 'general' - | 'gentle' - | 'sad' - | 'serious'; - -export interface SsmlOptions { - name: string; - pitch?: number; - rate?: number; - style?: StyleName; -} - -export const genSSML = (text: string, options: SsmlOptions) => { - const { name, style, rate, pitch } = options; - const ssml = [ - '', - ``, - style && ``, - rate && pitch && ``, - text, - rate && pitch && ``, - style && ``, - ``, - '', - ]; - return ssml.filter(Boolean).join(''); -}; diff --git a/src/const/api.ts b/src/const/api.ts index b6e7d68..4fe3a92 100644 --- a/src/const/api.ts +++ b/src/const/api.ts @@ -1,6 +1,7 @@ import urlJoin from 'url-join'; -export const MICROSOFT_SPEECH_PROXY_URL = process.env.MICROSOFT_SPEECH_PROXY_URL || ''; +export const MICROSOFT_SPEECH_PROXY_URL = + process.env.MICROSOFT_SPEECH_PROXY_URL || '/api/microsoft-speech'; export const AZURE_SPEECH_KEY = process.env.AZURE_SPEECH_KEY || ''; export const AZURE_SPEECH_REGION = process.env.AZURE_SPEECH_REGION || ''; export const OPENAI_API_KEY = process.env.OPENAI_API_KEY || ''; @@ -8,3 +9,7 @@ export const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL || 'https://api.ope export const OPENAI_TTS_URL = (api?: string) => urlJoin(api || OPENAI_PROXY_URL, 'audio/speech'); export const OPENAI_STT_URL = (api: string) => urlJoin(api || OPENAI_PROXY_URL, 'audio/transcriptions'); +export const EDDGE_PROXY_URL = + process.env.EDDGE_PROXY_URL || + 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1'; +export const EDDGE_API_TOKEN = process.env.EDDGE_API_TOKEN || '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; diff --git a/src/index.ts b/src/index.ts index b6a88eb..cee97e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ -export { fetchAzureSpeech } from './services/fetchAzureSpeech'; -export { fetchEdgeSpeech } from './services/fetchEdgeSpeech'; -export { fetchMicrosoftSpeech } from './services/fetchMicrosoftSpeech'; -export { fetchOpenaiSTT } from './services/fetchOpenaiSTT'; -export { fetchOpenaiTTS } from './services/fetchOpenaiTTS'; +export { type AzureSpeechOptions, fetchAzureSpeech } from './services/fetchAzureSpeech'; +export { type EdgeSpeechOptions, fetchEdgeSpeech } from './services/fetchEdgeSpeech'; +export { fetchMicrosoftSpeech, type MicrosoftSpeechOptions } from './services/fetchMicrosoftSpeech'; +export { fetchOpenaiSTT, type OpenaiSttOptions } from './services/fetchOpenaiSTT'; +export { fetchOpenaiTTS, type OpenaiTtsOptions } from './services/fetchOpenaiTTS'; export { useAzureSpeech } from './useAzureSpeech'; export { useEdgeSpeech } from './useEdgeSpeech'; export { useMicrosoftSpeech } from './useMicrosoftSpeech'; diff --git a/src/services/fetchAzureSpeech.ts b/src/services/fetchAzureSpeech.ts index 884d5f8..6aeba1d 100644 --- a/src/services/fetchAzureSpeech.ts +++ b/src/services/fetchAzureSpeech.ts @@ -9,8 +9,7 @@ import { } from 'microsoft-cognitiveservices-speech-sdk'; import { AZURE_SPEECH_KEY, AZURE_SPEECH_REGION } from '@/const/api'; - -import { type SsmlOptions, genSSML } from '../utils/genSSML'; +import { type SsmlOptions, genSSML } from '@/utils/genSSML'; export interface AzureSpeechOptions extends SsmlOptions { api: { diff --git a/src/services/fetchEdgeSpeech.ts b/src/services/fetchEdgeSpeech.ts index 866eaa2..1b57c86 100644 --- a/src/services/fetchEdgeSpeech.ts +++ b/src/services/fetchEdgeSpeech.ts @@ -1,14 +1,18 @@ import qs from 'query-string'; import { v4 as uuidv4 } from 'uuid'; -import { type SsmlOptions, genSSML } from '../utils/genSSML'; -import { genSendContent } from '../utils/genSendContent'; -import { getHeadersAndData } from '../utils/getHeadersAndData'; +import { EDDGE_API_TOKEN, EDDGE_PROXY_URL } from '@/const/api'; +import { type SsmlOptions, genSSML } from '@/utils/genSSML'; +import { genSendContent } from '@/utils/genSendContent'; +import { getHeadersAndData } from '@/utils/getHeadersAndData'; -const API = 'wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1'; -const TOKEN = '6A5AA1D4EAFF4E9FB37E23D68491D6F4'; - -export const fetchEdgeSpeech = async (text: string, options: SsmlOptions) => { +export interface EdgeSpeechOptions extends Pick { + api: { + key: string; + proxy: string; + }; +} +export const fetchEdgeSpeech = async (text: string, { api, ...options }: EdgeSpeechOptions) => { const connectId = uuidv4().replaceAll('-', ''); const date = new Date().toString(); const audioContext = new AudioContext(); @@ -18,9 +22,9 @@ export const fetchEdgeSpeech = async (text: string, options: SsmlOptions) => { qs.stringifyUrl({ query: { ConnectionId: connectId, - TrustedClientToken: TOKEN, + TrustedClientToken: api.key || EDDGE_API_TOKEN, }, - url: API, + url: api.proxy || EDDGE_PROXY_URL, }), ); ws.binaryType = 'arraybuffer'; diff --git a/src/services/fetchMicrosoftSpeech.ts b/src/services/fetchMicrosoftSpeech.ts index 87051ec..e303c92 100644 --- a/src/services/fetchMicrosoftSpeech.ts +++ b/src/services/fetchMicrosoftSpeech.ts @@ -1,8 +1,8 @@ -import qs from 'query-string'; +import { v4 as uuidv4 } from 'uuid'; import { MICROSOFT_SPEECH_PROXY_URL } from '@/const/api'; - -import { type SsmlOptions } from '../utils/genSSML'; +import { type SsmlOptions } from '@/utils/genSSML'; +import { genSSML } from '@/utils/genSSML'; export interface MicrosoftSpeechOptions extends SsmlOptions { api?: string; @@ -12,12 +12,39 @@ export const fetchMicrosoftSpeech = async ( text: string, { api, ...options }: MicrosoftSpeechOptions, ): Promise => { - const response: Response = await fetch( - qs.stringifyUrl({ - query: { text, ...options }, - url: api || MICROSOFT_SPEECH_PROXY_URL, - }), - ); + const data = JSON.stringify({ + offsetInPlainText: 0, + properties: { + SpeakTriggerSource: 'AccTuningPagePlayButton', + }, + ssml: genSSML(text, options), + ttsAudioFormat: 'audio-24khz-160kbitrate-mono-mp3', + }); + + const DEFAULT_HEADERS = new Headers({ + 'accept': '*/*', + 'accept-language': 'zh-CN,zh;q=0.9', + 'authority': 'southeastasia.api.speech.microsoft.com', + 'content-type': 'application/json', + 'customvoiceconnectionid': uuidv4(), + 'origin': 'https://speech.microsoft.com', + 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-site', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36', + }); + + const response: Response = await fetch(api || MICROSOFT_SPEECH_PROXY_URL, { + body: data, + headers: DEFAULT_HEADERS, + method: 'POST', + // @ts-ignore + responseType: 'arraybuffer', + }); if (!response.ok) { throw new Error('Network response was not ok'); diff --git a/src/services/fetchOpenaiSTT.ts b/src/services/fetchOpenaiSTT.ts index 8589125..fc36555 100644 --- a/src/services/fetchOpenaiSTT.ts +++ b/src/services/fetchOpenaiSTT.ts @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { OPENAI_API_KEY, OPENAI_STT_URL } from '@/const/api'; -export interface OpenaiTtsOptions { +export interface OpenaiSttOptions { api: { key: string; proxy: string; @@ -13,7 +13,7 @@ export interface OpenaiTtsOptions { // 纯文本生成语音 export const fetchOpenaiSTT = async ( speech: Blob, - { api, model = 'whisper-1' }: OpenaiTtsOptions, + { api, model = 'whisper-1' }: OpenaiSttOptions, ): Promise => { const key = api.key || OPENAI_API_KEY; const url = OPENAI_STT_URL(api.proxy); diff --git a/src/services/fetchOpenaiTTS.ts b/src/services/fetchOpenaiTTS.ts index 2ea1c41..89d106c 100644 --- a/src/services/fetchOpenaiTTS.ts +++ b/src/services/fetchOpenaiTTS.ts @@ -1,10 +1,9 @@ import { OPENAI_API_KEY, OPENAI_TTS_URL } from '@/const/api'; - -import { type SsmlOptions } from '../utils/genSSML'; +import { type SsmlOptions } from '@/utils/genSSML'; export type OpenaiVoice = 'alloy' | 'echo' | 'fable' | 'onyx' | 'nova' | 'shimmer'; -export interface OpenaiTtsOptions extends SsmlOptions { +export interface OpenaiTtsOptions extends Pick { api: { key: string; proxy: string; diff --git a/src/useAzureSpeech/index.ts b/src/useAzureSpeech/index.ts index f77d445..9cc3a36 100644 --- a/src/useAzureSpeech/index.ts +++ b/src/useAzureSpeech/index.ts @@ -1,17 +1,20 @@ import { useState } from 'react'; import useSWR from 'swr'; -import { AzureSpeechOptions, fetchAzureSpeech } from '../services/fetchAzureSpeech'; +import { AzureSpeechOptions, fetchAzureSpeech } from '@/services/fetchAzureSpeech'; -export const useAzureSpeech = (defaultText: string, options: AzureSpeechOptions) => { +export const useAzureSpeech = ( + defaultText: string, + { api, name, style, pitch, rate }: AzureSpeechOptions, +) => { const [data, setDate] = useState(); const [text, setText] = useState(defaultText); const [shouldFetch, setShouldFetch] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const { isLoading } = useSWR( - shouldFetch ? [options.name, text].join('-') : null, - () => fetchAzureSpeech(text, options), + shouldFetch ? [name, text].join('-') : null, + () => fetchAzureSpeech(text, { api, name, pitch, rate, style }), { onError: () => setShouldFetch(false), onSuccess: (audioBufferSource) => { diff --git a/src/useEdgeSpeech/demos/index.tsx b/src/useEdgeSpeech/demos/index.tsx index 092876b..66b5d64 100644 --- a/src/useEdgeSpeech/demos/index.tsx +++ b/src/useEdgeSpeech/demos/index.tsx @@ -8,6 +8,21 @@ const defaultText = '这是一段使用 Edge Speech 的语音演示'; export default () => { const store = useCreateStore(); + + const api: any = useControls( + { + key: { + label: 'EDDGE_API_TOKEN', + value: '', + }, + proxy: { + label: 'EDDGE_PROXY_URL', + value: '', + }, + }, + { store }, + ); + const options: any = useControls( { name: { @@ -17,7 +32,11 @@ export default () => { }, { store }, ); - const { setText, isLoading, isPlaying, start, stop } = useEdgeSpeech(defaultText, options); + + const { setText, isLoading, isPlaying, start, stop } = useEdgeSpeech(defaultText, { + api, + ...options, + }); return ( diff --git a/src/useEdgeSpeech/index.ts b/src/useEdgeSpeech/index.ts index 8f3b8db..a52412e 100644 --- a/src/useEdgeSpeech/index.ts +++ b/src/useEdgeSpeech/index.ts @@ -1,18 +1,17 @@ import { useState } from 'react'; import useSWR from 'swr'; -import { fetchEdgeSpeech } from '../services/fetchEdgeSpeech'; -import { SsmlOptions } from '../utils/genSSML'; +import { EdgeSpeechOptions, fetchEdgeSpeech } from '@/services/fetchEdgeSpeech'; -export const useEdgeSpeech = (defaultText: string, options: SsmlOptions) => { +export const useEdgeSpeech = (defaultText: string, { api, name }: EdgeSpeechOptions) => { const [data, setDate] = useState(); const [text, setText] = useState(defaultText); const [shouldFetch, setShouldFetch] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const { isLoading } = useSWR( - shouldFetch ? [options.name, text].join('-') : null, - () => fetchEdgeSpeech(text, options), + shouldFetch ? [name, text].join('-') : null, + () => fetchEdgeSpeech(text, { api, name }), { onError: () => setShouldFetch(false), onSuccess: (audioBufferSource) => { diff --git a/src/useMicrosoftSpeech/index.ts b/src/useMicrosoftSpeech/index.ts index 10cec68..b8c9d37 100644 --- a/src/useMicrosoftSpeech/index.ts +++ b/src/useMicrosoftSpeech/index.ts @@ -1,20 +1,20 @@ import { useState } from 'react'; import useSWR from 'swr'; -import { - type MicrosoftSpeechOptions, - fetchMicrosoftSpeech, -} from '../services/fetchMicrosoftSpeech'; +import { type MicrosoftSpeechOptions, fetchMicrosoftSpeech } from '@/services/fetchMicrosoftSpeech'; -export const useMicrosoftSpeech = (defaultText: string, options: MicrosoftSpeechOptions) => { +export const useMicrosoftSpeech = ( + defaultText: string, + { api, name, pitch, rate, style }: MicrosoftSpeechOptions, +) => { const [data, setDate] = useState(); const [text, setText] = useState(defaultText); const [shouldFetch, setShouldFetch] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const { isLoading } = useSWR( - shouldFetch ? [options.name, text].join('-') : null, - () => fetchMicrosoftSpeech(text, options), + shouldFetch ? [name, text].join('-') : null, + () => fetchMicrosoftSpeech(text, { api, name, pitch, rate, style }), { onError: () => setShouldFetch(false), onSuccess: (audioBufferSource) => { diff --git a/src/useOpenaiTTS/demos/index.tsx b/src/useOpenaiTTS/demos/index.tsx index 333da25..28efe3f 100644 --- a/src/useOpenaiTTS/demos/index.tsx +++ b/src/useOpenaiTTS/demos/index.tsx @@ -15,7 +15,7 @@ export default () => { label: 'OPENAI_API_KEY', value: '', }, - region: { + proxy: { label: 'OPENAI_PROXY_URL', value: '', }, @@ -29,34 +29,6 @@ export default () => { options: getOpenaiVoiceList(), value: 'alloy', }, - pitch: { - max: 1, - min: -1, - step: 0.1, - value: 0, - }, - rate: { - max: 1, - min: -1, - step: 0.1, - value: 0, - }, - style: { - options: [ - 'affectionate', - 'angry', - 'calm', - 'cheerful', - 'disgruntled', - 'embarrassed', - 'fearful', - 'general', - 'gentle', - 'sad', - 'serious', - ], - value: 'general', - }, }, { store }, ); diff --git a/src/useOpenaiTTS/index.ts b/src/useOpenaiTTS/index.ts index f2834f5..e6a4bb1 100644 --- a/src/useOpenaiTTS/index.ts +++ b/src/useOpenaiTTS/index.ts @@ -1,17 +1,17 @@ import { useState } from 'react'; import useSWR from 'swr'; -import { type OpenaiTtsOptions, fetchOpenaiTTS } from '../services/fetchOpenaiTTS'; +import { type OpenaiTtsOptions, fetchOpenaiTTS } from '@/services/fetchOpenaiTTS'; -export const useOpenaiTTS = (defaultText: string, options: OpenaiTtsOptions) => { +export const useOpenaiTTS = (defaultText: string, { api, name }: OpenaiTtsOptions) => { const [data, setDate] = useState(); const [text, setText] = useState(defaultText); const [shouldFetch, setShouldFetch] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const { isLoading } = useSWR( - shouldFetch ? [options.name, text].join('-') : null, - () => fetchOpenaiTTS(text, options), + shouldFetch ? [name, text].join('-') : null, + () => fetchOpenaiTTS(text, { api, name }), { onError: () => setShouldFetch(false), onSuccess: (audioBufferSource) => { diff --git a/src/useSpeechSynthes/index.ts b/src/useSpeechSynthes/index.ts index a8f6c72..95a700d 100644 --- a/src/useSpeechSynthes/index.ts +++ b/src/useSpeechSynthes/index.ts @@ -1,19 +1,19 @@ import { useMemo, useState } from 'react'; -import { SsmlOptions } from '../utils/genSSML'; +import { SsmlOptions } from '@/utils/genSSML'; -export const useSpeechSynthes = (defaultText: string, options: SsmlOptions) => { +export const useSpeechSynthes = (defaultText: string, { name, rate, pitch }: SsmlOptions) => { const [voiceList, setVoiceList] = useState(speechSynthesis.getVoices()); const [text, setText] = useState(defaultText); const [isLoading, setIsLoading] = useState(false); const speechSynthesisUtterance = useMemo(() => { const utterance = new SpeechSynthesisUtterance(text); - utterance.voice = voiceList.find((item) => item.name === options.name) as any; - if (options.pitch) utterance.pitch = options.pitch * 10; - if (options.rate) utterance.rate = options.rate * 10; + utterance.voice = voiceList.find((item) => item.name === name) as any; + if (pitch) utterance.pitch = pitch * 10; + if (rate) utterance.rate = rate * 10; return utterance; - }, [text, voiceList, options]); + }, [text, voiceList, rate, pitch, name]); speechSynthesis.onvoiceschanged = () => { setVoiceList(speechSynthesis.getVoices());