| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import type { |
| BuiltInTTSProviderId, |
| TTSProviderId, |
| TTSProviderConfig, |
| TTSVoiceInfo, |
| BuiltInASRProviderId, |
| ASRProviderId, |
| ASRProviderConfig, |
| } from './types'; |
| import { |
| VOXCPM_AUTO_VOICE, |
| VOXCPM_AUTO_VOICE_ID, |
| VOXCPM_TTS_PROVIDER_ID, |
| VOXCPM_VLLM_MODEL_ID, |
| } from './voxcpm'; |
|
|
| |
| |
| |
| |
| export const CUSTOM_ASR_DEFAULT_LANGUAGES = [ |
| 'auto', |
| 'zh', |
| 'en', |
| 'ja', |
| 'ko', |
| 'es', |
| 'fr', |
| 'de', |
| 'ru', |
| 'ar', |
| 'pt', |
| 'it', |
| 'hi', |
| ]; |
|
|
| |
| |
| |
| |
| |
| |
| export const MINIMAX_TTS_MODELS = [ |
| { id: 'speech-2.8-hd', name: 'Speech 2.8 HD' }, |
| { id: 'speech-2.8-turbo', name: 'Speech 2.8 Turbo' }, |
| { id: 'speech-2.6-hd', name: 'Speech 2.6 HD' }, |
| { id: 'speech-2.6-turbo', name: 'Speech 2.6 Turbo' }, |
| { id: 'speech-02-hd', name: 'Speech 02 HD' }, |
| { id: 'speech-02-turbo', name: 'Speech 02 Turbo' }, |
| ] as const; |
|
|
| export const TTS_PROVIDERS: Record<BuiltInTTSProviderId, TTSProviderConfig> = { |
| 'openai-tts': { |
| id: 'openai-tts', |
| name: 'OpenAI TTS', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://api.openai.com/v1', |
| icon: '/logos/openai.svg', |
| models: [ |
| { id: 'gpt-4o-mini-tts', name: 'GPT-4o Mini TTS' }, |
| { id: 'tts-1', name: 'TTS-1' }, |
| { id: 'tts-1-hd', name: 'TTS-1 HD' }, |
| ], |
| defaultModelId: 'gpt-4o-mini-tts', |
| voices: [ |
| |
| { |
| id: 'marin', |
| name: 'Marin', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceMarin', |
| compatibleModels: ['gpt-4o-mini-tts'], |
| }, |
| { |
| id: 'cedar', |
| name: 'Cedar', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceCedar', |
| compatibleModels: ['gpt-4o-mini-tts'], |
| }, |
| |
| { |
| id: 'alloy', |
| name: 'Alloy', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceAlloy', |
| }, |
| { |
| id: 'ash', |
| name: 'Ash', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceAsh', |
| }, |
| { |
| id: 'ballad', |
| name: 'Ballad', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceBallad', |
| }, |
| { |
| id: 'coral', |
| name: 'Coral', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceCoral', |
| }, |
| { |
| id: 'echo', |
| name: 'Echo', |
| language: 'en', |
| gender: 'male', |
| description: 'voiceEcho', |
| }, |
| { |
| id: 'fable', |
| name: 'Fable', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceFable', |
| }, |
| { |
| id: 'nova', |
| name: 'Nova', |
| language: 'en', |
| gender: 'female', |
| description: 'voiceNova', |
| }, |
| { |
| id: 'onyx', |
| name: 'Onyx', |
| language: 'en', |
| gender: 'male', |
| description: 'voiceOnyx', |
| }, |
| { |
| id: 'sage', |
| name: 'Sage', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceSage', |
| }, |
| { |
| id: 'shimmer', |
| name: 'Shimmer', |
| language: 'en', |
| gender: 'female', |
| description: 'voiceShimmer', |
| }, |
| { |
| id: 'verse', |
| name: 'Verse', |
| language: 'en', |
| gender: 'neutral', |
| description: 'voiceVerse', |
| }, |
| ], |
| supportedFormats: ['mp3', 'opus', 'aac', 'flac'], |
| speedRange: { min: 0.25, max: 4.0, default: 1.0 }, |
| }, |
|
|
| 'azure-tts': { |
| id: 'azure-tts', |
| name: 'Azure TTS', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://{region}.tts.speech.microsoft.com', |
| icon: '/logos/azure.svg', |
| models: [], |
| defaultModelId: '', |
| voices: [ |
| { |
| id: 'zh-CN-XiaoxiaoNeural', |
| name: '晓晓 (女)', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh-CN-YunxiNeural', |
| name: '云希 (男)', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh-CN-XiaoyiNeural', |
| name: '晓伊 (女)', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh-CN-YunjianNeural', |
| name: '云健 (男)', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'en-US-JennyNeural', |
| name: 'Jenny', |
| language: 'en-US', |
| gender: 'female', |
| }, |
| { id: 'en-US-GuyNeural', name: 'Guy', language: 'en-US', gender: 'male' }, |
| ], |
| supportedFormats: ['mp3', 'wav', 'ogg'], |
| speedRange: { min: 0.5, max: 2.0, default: 1.0 }, |
| }, |
|
|
| 'glm-tts': { |
| id: 'glm-tts', |
| name: 'GLM TTS', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4', |
| icon: '/logos/glm.svg', |
| models: [{ id: 'glm-tts', name: 'GLM TTS' }], |
| defaultModelId: 'glm-tts', |
| voices: [ |
| { |
| id: 'tongtong', |
| name: '彤彤', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceTongtong', |
| }, |
| { |
| id: 'chuichui', |
| name: '锤锤', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceChuichui', |
| }, |
| { |
| id: 'xiaochen', |
| name: '小陈', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceXiaochen', |
| }, |
| { |
| id: 'jam', |
| name: 'Jam', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceJam', |
| }, |
| { |
| id: 'kazi', |
| name: 'Kazi', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceKazi', |
| }, |
| { |
| id: 'douji', |
| name: '豆几', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceDouji', |
| }, |
| { |
| id: 'luodo', |
| name: '罗多', |
| language: 'zh', |
| gender: 'neutral', |
| description: 'glmVoiceLuodo', |
| }, |
| ], |
| supportedFormats: ['mp3', 'wav'], |
| speedRange: { min: 0.5, max: 2.0, default: 1.0 }, |
| }, |
|
|
| 'qwen-tts': { |
| id: 'qwen-tts', |
| name: 'Qwen TTS (阿里云百炼)', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://dashscope.aliyuncs.com/api/v1', |
| icon: '/logos/bailian.svg', |
| models: [ |
| { id: 'qwen3-tts-flash', name: 'Qwen3 TTS Flash' }, |
| { id: 'qwen3-tts-instruct-flash', name: 'Qwen3 TTS Instruct Flash' }, |
| { id: 'qwen-tts', name: 'Qwen TTS' }, |
| ], |
| defaultModelId: 'qwen3-tts-flash', |
| voices: [ |
| |
| { |
| id: 'Cherry', |
| name: '芊悦 (Cherry)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceCherry', |
| }, |
| { |
| id: 'Serena', |
| name: '苏瑶 (Serena)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceSerena', |
| }, |
| { |
| id: 'Ethan', |
| name: '晨煦 (Ethan)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceEthan', |
| }, |
| { |
| id: 'Chelsie', |
| name: '千雪 (Chelsie)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceChelsie', |
| }, |
| { |
| id: 'Momo', |
| name: '茉兔 (Momo)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceMomo', |
| }, |
| { |
| id: 'Vivian', |
| name: '十三 (Vivian)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceVivian', |
| }, |
| { |
| id: 'Moon', |
| name: '月白 (Moon)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceMoon', |
| }, |
| { |
| id: 'Maia', |
| name: '四月 (Maia)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceMaia', |
| }, |
| { |
| id: 'Kai', |
| name: '凯 (Kai)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceKai', |
| }, |
| { |
| id: 'Nofish', |
| name: '不吃鱼 (Nofish)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceNofish', |
| }, |
| { |
| id: 'Bella', |
| name: '萌宝 (Bella)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceBella', |
| }, |
| { |
| id: 'Jennifer', |
| name: '詹妮弗 (Jennifer)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceJennifer', |
| }, |
| { |
| id: 'Ryan', |
| name: '甜茶 (Ryan)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceRyan', |
| }, |
| { |
| id: 'Katerina', |
| name: '卡捷琳娜 (Katerina)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceKaterina', |
| }, |
| { |
| id: 'Aiden', |
| name: '艾登 (Aiden)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceAiden', |
| }, |
| { |
| id: 'Eldric Sage', |
| name: '沧明子 (Eldric Sage)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceEldricSage', |
| }, |
| { |
| id: 'Mia', |
| name: '乖小妹 (Mia)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceMia', |
| }, |
| { |
| id: 'Mochi', |
| name: '沙小弥 (Mochi)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceMochi', |
| }, |
| { |
| id: 'Bellona', |
| name: '燕铮莺 (Bellona)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceBellona', |
| }, |
| { |
| id: 'Vincent', |
| name: '田叔 (Vincent)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceVincent', |
| }, |
| { |
| id: 'Bunny', |
| name: '萌小姬 (Bunny)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceBunny', |
| }, |
| { |
| id: 'Neil', |
| name: '阿闻 (Neil)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceNeil', |
| }, |
| { |
| id: 'Elias', |
| name: '墨讲师 (Elias)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceElias', |
| }, |
| { |
| id: 'Arthur', |
| name: '徐大爷 (Arthur)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceArthur', |
| }, |
| { |
| id: 'Nini', |
| name: '邻家妹妹 (Nini)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceNini', |
| }, |
| { |
| id: 'Ebona', |
| name: '诡婆婆 (Ebona)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceEbona', |
| }, |
| { |
| id: 'Seren', |
| name: '小婉 (Seren)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceSeren', |
| }, |
| { |
| id: 'Pip', |
| name: '顽屁小孩 (Pip)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoicePip', |
| }, |
| { |
| id: 'Stella', |
| name: '少女阿月 (Stella)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceStella', |
| }, |
| |
| { |
| id: 'Bodega', |
| name: '博德加 (Bodega)', |
| language: 'es', |
| gender: 'male', |
| description: 'qwenVoiceBodega', |
| }, |
| { |
| id: 'Sonrisa', |
| name: '索尼莎 (Sonrisa)', |
| language: 'es', |
| gender: 'female', |
| description: 'qwenVoiceSonrisa', |
| }, |
| { |
| id: 'Alek', |
| name: '阿列克 (Alek)', |
| language: 'ru', |
| gender: 'male', |
| description: 'qwenVoiceAlek', |
| }, |
| { |
| id: 'Dolce', |
| name: '多尔切 (Dolce)', |
| language: 'it', |
| gender: 'male', |
| description: 'qwenVoiceDolce', |
| }, |
| { |
| id: 'Sohee', |
| name: '素熙 (Sohee)', |
| language: 'ko', |
| gender: 'female', |
| description: 'qwenVoiceSohee', |
| }, |
| { |
| id: 'Ono Anna', |
| name: '小野杏 (Ono Anna)', |
| language: 'ja', |
| gender: 'female', |
| description: 'qwenVoiceOnoAnna', |
| }, |
| { |
| id: 'Lenn', |
| name: '莱恩 (Lenn)', |
| language: 'de', |
| gender: 'male', |
| description: 'qwenVoiceLenn', |
| }, |
| { |
| id: 'Emilien', |
| name: '埃米尔安 (Emilien)', |
| language: 'fr', |
| gender: 'male', |
| description: 'qwenVoiceEmilien', |
| }, |
| { |
| id: 'Andre', |
| name: '安德雷 (Andre)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceAndre', |
| }, |
| { |
| id: 'Radio Gol', |
| name: '拉迪奥·戈尔 (Radio Gol)', |
| language: 'pt', |
| gender: 'male', |
| description: 'qwenVoiceRadioGol', |
| }, |
| |
| { |
| id: 'Jada', |
| name: '上海-阿珍 (Jada)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceJada', |
| }, |
| { |
| id: 'Dylan', |
| name: '北京-晓东 (Dylan)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceDylan', |
| }, |
| { |
| id: 'Li', |
| name: '南京-老李 (Li)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceLi', |
| }, |
| { |
| id: 'Marcus', |
| name: '陕西-秦川 (Marcus)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceMarcus', |
| }, |
| { |
| id: 'Roy', |
| name: '闽南-阿杰 (Roy)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceRoy', |
| }, |
| { |
| id: 'Peter', |
| name: '天津-李彼得 (Peter)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoicePeter', |
| }, |
| { |
| id: 'Sunny', |
| name: '四川-晴儿 (Sunny)', |
| language: 'zh-CN', |
| gender: 'female', |
| description: 'qwenVoiceSunny', |
| }, |
| { |
| id: 'Eric', |
| name: '四川-程川 (Eric)', |
| language: 'zh-CN', |
| gender: 'male', |
| description: 'qwenVoiceEric', |
| }, |
| { |
| id: 'Rocky', |
| name: '粤语-阿强 (Rocky)', |
| language: 'zh-HK', |
| gender: 'male', |
| description: 'qwenVoiceRocky', |
| }, |
| { |
| id: 'Kiki', |
| name: '粤语-阿清 (Kiki)', |
| language: 'zh-HK', |
| gender: 'female', |
| description: 'qwenVoiceKiki', |
| }, |
| ], |
| supportedFormats: ['mp3', 'wav', 'pcm'], |
| }, |
|
|
| 'minimax-tts': { |
| id: 'minimax-tts', |
| name: 'MiniMax TTS', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://api.minimaxi.com', |
| icon: '/logos/minimax.svg', |
| models: MINIMAX_TTS_MODELS.map((m) => ({ id: m.id, name: m.name })), |
| defaultModelId: 'speech-2.8-hd', |
| voices: [ |
| |
| { |
| id: 'female-yujie', |
| name: '御姐音色', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'male-qn-jingying', |
| name: '精英青年', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'female-shaonv', |
| name: '少女音色', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'Chinese (Mandarin)_Gentleman', |
| name: '温润男声', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'Chinese (Mandarin)_News_Anchor', |
| name: '新闻女声', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'Chinese (Mandarin)_Warm_Girl', |
| name: '温暖少女', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'Chinese (Mandarin)_Radio_Host', |
| name: '电台男主播', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| |
| { |
| id: 'English_Trustworthy_Man', |
| name: 'Trustworthy Man', |
| language: 'en-US', |
| gender: 'male', |
| }, |
| { |
| id: 'English_Graceful_Lady', |
| name: 'Graceful Lady', |
| language: 'en-US', |
| gender: 'female', |
| }, |
| { |
| id: 'English_expressive_narrator', |
| name: 'Expressive Narrator', |
| language: 'en-US', |
| gender: 'neutral', |
| }, |
| ], |
| supportedFormats: ['mp3', 'wav', 'flac', 'pcm'], |
| speedRange: { |
| min: 0.5, |
| max: 2.0, |
| default: 1.0, |
| }, |
| }, |
|
|
| 'voxcpm-tts': { |
| id: VOXCPM_TTS_PROVIDER_ID, |
| name: 'VoxCPM2', |
| requiresApiKey: false, |
| defaultBaseUrl: 'http://127.0.0.1:8000', |
| icon: '/logos/voxcpm-icon.png', |
| models: [{ id: VOXCPM_VLLM_MODEL_ID, name: 'VoxCPM2' }], |
| defaultModelId: VOXCPM_VLLM_MODEL_ID, |
| voices: [VOXCPM_AUTO_VOICE], |
| supportedFormats: ['mp3', 'wav'], |
| speedRange: { |
| min: 0.5, |
| max: 2.0, |
| default: 1.0, |
| }, |
| }, |
|
|
| 'doubao-tts': { |
| id: 'doubao-tts', |
| name: '豆包 TTS 2.0(火山引擎)', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://openspeech.bytedance.com/api/v3/tts', |
| icon: '/logos/doubao.svg', |
| models: [], |
| defaultModelId: '', |
| voices: [ |
| { id: 'zh_female_vv_uranus_bigtts', name: 'Vivi 2.0', language: 'zh-CN', gender: 'female' }, |
| { |
| id: 'zh_female_xiaohe_uranus_bigtts', |
| name: '小何 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_male_m191_uranus_bigtts', |
| name: '云舟 2.0', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh_male_taocheng_uranus_bigtts', |
| name: '小天 2.0', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh_male_liufei_uranus_bigtts', |
| name: '刘飞 2.0', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh_female_qingxinnvsheng_uranus_bigtts', |
| name: '清新女声 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_female_cancan_uranus_bigtts', |
| name: '知性灿灿 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_female_shuangkuaisisi_uranus_bigtts', |
| name: '爽快思思 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_female_tianmeixiaoyuan_uranus_bigtts', |
| name: '甜美小源 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_female_linjianvhai_uranus_bigtts', |
| name: '邻家女孩 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_male_shaonianzixin_uranus_bigtts', |
| name: '少年梓辛 2.0', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh_male_ruyayichen_uranus_bigtts', |
| name: '儒雅逸辰 2.0', |
| language: 'zh-CN', |
| gender: 'male', |
| }, |
| { |
| id: 'zh_female_yingyujiaoxue_uranus_bigtts', |
| name: 'Tina老师 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { |
| id: 'zh_female_kefunvsheng_uranus_bigtts', |
| name: '暖阳女声 2.0', |
| language: 'zh-CN', |
| gender: 'female', |
| }, |
| { id: 'en_male_tim_uranus_bigtts', name: 'Tim', language: 'en-US', gender: 'male' }, |
| { id: 'en_female_dacey_uranus_bigtts', name: 'Dacey', language: 'en-US', gender: 'female' }, |
| { |
| id: 'en_female_stokie_uranus_bigtts', |
| name: 'Stokie', |
| language: 'en-US', |
| gender: 'female', |
| }, |
| ], |
| supportedFormats: ['mp3'], |
| speedRange: { min: 0.5, max: 2.0, default: 1.0 }, |
| }, |
| 'elevenlabs-tts': { |
| id: 'elevenlabs-tts', |
| name: 'ElevenLabs TTS', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://api.elevenlabs.io/v1', |
| icon: '/logos/elevenlabs.svg', |
| models: [ |
| { id: 'eleven_multilingual_v2', name: 'Multilingual v2' }, |
| { id: 'eleven_flash_v2_5', name: 'Flash v2.5' }, |
| { id: 'eleven_flash_v2', name: 'Flash v2' }, |
| ], |
| defaultModelId: 'eleven_multilingual_v2', |
| |
| voices: [ |
| { |
| id: 'EXAVITQu4vr4xnSDxMaL', |
| name: 'Sarah', |
| language: 'en-US', |
| gender: 'female', |
| description: 'Confident and warm professional voice for clear narration', |
| }, |
| { |
| id: 'Xb7hH8MSUJpSbSDYk0k2', |
| name: 'Alice', |
| language: 'en-GB', |
| gender: 'female', |
| description: 'Clear and engaging British educator voice for e-learning', |
| }, |
| { |
| id: 'XrExE9yKIg1WjnnlVkGX', |
| name: 'Matilda', |
| language: 'en-US', |
| gender: 'female', |
| description: 'Knowledgeable and upbeat voice suited for lectures', |
| }, |
| { |
| id: 'CwhRBWXzGAHq8TQ4Fs17', |
| name: 'Roger', |
| language: 'en-US', |
| gender: 'male', |
| description: 'Laid-back but resonant male voice for friendly lessons', |
| }, |
| { |
| id: 'cjVigY5qzO86Huf0OWal', |
| name: 'Eric', |
| language: 'en-US', |
| gender: 'male', |
| description: 'Smooth and trustworthy voice for polished classroom audio', |
| }, |
| { |
| id: 'onwK4e9ZLuTAKqWW03F9', |
| name: 'Daniel', |
| language: 'en-GB', |
| gender: 'male', |
| description: 'Steady British broadcaster voice for formal explanations', |
| }, |
| { |
| id: 'SAz9YHcvj6GT2YYXdXww', |
| name: 'River', |
| language: 'en-US', |
| gender: 'neutral', |
| description: 'Relaxed and informative neutral voice for general narration', |
| }, |
| ], |
| supportedFormats: ['mp3', 'opus', 'pcm', 'wav', 'ulaw', 'alaw'], |
| speedRange: { min: 0.7, max: 1.2, default: 1.0 }, |
| }, |
|
|
| 'browser-native-tts': { |
| id: 'browser-native-tts', |
| name: '浏览器原生 (Web Speech API)', |
| requiresApiKey: false, |
| icon: '/logos/browser.svg', |
| models: [], |
| defaultModelId: '', |
| voices: [ |
| |
| |
| { id: 'default', name: '默认', language: 'zh-CN', gender: 'neutral' }, |
| ], |
| supportedFormats: ['browser'], |
| speedRange: { min: 0.1, max: 10.0, default: 1.0 }, |
| }, |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| export const ASR_PROVIDERS: Record<BuiltInASRProviderId, ASRProviderConfig> = { |
| 'openai-whisper': { |
| id: 'openai-whisper', |
| name: 'OpenAI Whisper', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://api.openai.com/v1', |
| icon: '/logos/openai.svg', |
| models: [ |
| { id: 'gpt-4o-mini-transcribe', name: 'GPT-4o Mini Transcribe' }, |
| { id: 'gpt-4o-transcribe', name: 'GPT-4o Transcribe' }, |
| { id: 'whisper-1', name: 'Whisper-1' }, |
| ], |
| defaultModelId: 'gpt-4o-mini-transcribe', |
| supportedLanguages: [ |
| |
| |
| 'auto', |
| |
| 'zh', |
| 'en', |
| 'ja', |
| 'ko', |
| 'es', |
| 'fr', |
| 'de', |
| 'ru', |
| 'ar', |
| 'pt', |
| 'it', |
| 'hi', |
| |
| 'af', |
| 'hy', |
| 'az', |
| 'be', |
| 'bs', |
| 'bg', |
| 'ca', |
| 'hr', |
| 'cs', |
| 'da', |
| 'nl', |
| 'et', |
| 'fi', |
| 'gl', |
| 'el', |
| 'he', |
| 'hu', |
| 'is', |
| 'id', |
| 'kn', |
| 'kk', |
| 'lv', |
| 'lt', |
| 'mk', |
| 'ms', |
| 'mr', |
| 'mi', |
| 'ne', |
| 'no', |
| 'fa', |
| 'pl', |
| 'ro', |
| 'sr', |
| 'sk', |
| 'sl', |
| 'sw', |
| 'sv', |
| 'tl', |
| 'ta', |
| 'th', |
| 'tr', |
| 'uk', |
| 'ur', |
| 'vi', |
| 'cy', |
| ], |
| supportedFormats: ['mp3', 'mp4', 'mpeg', 'mpga', 'm4a', 'wav', 'webm'], |
| }, |
|
|
| 'qwen-asr': { |
| id: 'qwen-asr', |
| name: 'Qwen ASR (阿里云百炼)', |
| requiresApiKey: true, |
| defaultBaseUrl: 'https://dashscope.aliyuncs.com/api/v1', |
| icon: '/logos/bailian.svg', |
| models: [{ id: 'qwen3-asr-flash', name: 'Qwen3 ASR Flash' }], |
| defaultModelId: 'qwen3-asr-flash', |
| supportedLanguages: [ |
| |
| |
| 'auto', |
| |
| 'zh', |
| 'yue', |
| 'en', |
| 'ja', |
| 'ko', |
| 'de', |
| 'fr', |
| 'ru', |
| 'es', |
| 'pt', |
| 'ar', |
| 'it', |
| 'hi', |
| |
| 'cs', |
| 'da', |
| 'fi', |
| 'fil', |
| 'id', |
| 'is', |
| 'ms', |
| 'no', |
| 'pl', |
| 'sv', |
| 'th', |
| 'tr', |
| 'uk', |
| 'vi', |
| ], |
| supportedFormats: ['mp3', 'wav', 'webm', 'm4a', 'flac'], |
| }, |
|
|
| 'browser-native': { |
| id: 'browser-native', |
| name: '浏览器原生 ASR (Web Speech API)', |
| requiresApiKey: false, |
| icon: '/logos/browser.svg', |
| models: [], |
| defaultModelId: '', |
| supportedLanguages: [ |
| |
| 'zh-CN', |
| 'zh-TW', |
| 'zh-HK', |
| 'yue-Hant-HK', |
| |
| 'en-US', |
| 'en-GB', |
| 'en-AU', |
| 'en-CA', |
| 'en-IN', |
| 'en-NZ', |
| 'en-ZA', |
| |
| 'ja-JP', |
| 'ko-KR', |
| |
| 'de-DE', |
| 'fr-FR', |
| 'es-ES', |
| 'es-MX', |
| 'es-AR', |
| 'es-CO', |
| 'it-IT', |
| 'pt-BR', |
| 'pt-PT', |
| 'ru-RU', |
| 'nl-NL', |
| 'pl-PL', |
| 'cs-CZ', |
| 'da-DK', |
| 'fi-FI', |
| 'sv-SE', |
| 'no-NO', |
| 'tr-TR', |
| 'el-GR', |
| 'hu-HU', |
| 'ro-RO', |
| 'sk-SK', |
| 'bg-BG', |
| 'hr-HR', |
| 'ca-ES', |
| |
| 'ar-SA', |
| 'ar-EG', |
| 'he-IL', |
| 'hi-IN', |
| 'th-TH', |
| 'vi-VN', |
| 'id-ID', |
| 'ms-MY', |
| 'fil-PH', |
| |
| 'af-ZA', |
| 'uk-UA', |
| ], |
| supportedFormats: ['webm'], |
| }, |
| }; |
|
|
| |
| |
| |
| |
| export const DEFAULT_TTS_VOICES: Record<BuiltInTTSProviderId, string> = { |
| 'openai-tts': 'alloy', |
| 'azure-tts': 'zh-CN-XiaoxiaoNeural', |
| 'glm-tts': 'tongtong', |
| 'qwen-tts': 'Cherry', |
| 'voxcpm-tts': VOXCPM_AUTO_VOICE_ID, |
| 'doubao-tts': 'zh_female_vv_uranus_bigtts', |
| 'elevenlabs-tts': 'EXAVITQu4vr4xnSDxMaL', |
| 'minimax-tts': 'female-yujie', |
| 'browser-native-tts': 'default', |
| }; |
|
|
| export const DEFAULT_TTS_MODELS: Record<BuiltInTTSProviderId, string> = { |
| 'openai-tts': 'gpt-4o-mini-tts', |
| 'azure-tts': '', |
| 'glm-tts': 'glm-tts', |
| 'qwen-tts': 'qwen3-tts-flash', |
| 'voxcpm-tts': VOXCPM_VLLM_MODEL_ID, |
| 'doubao-tts': '', |
| 'elevenlabs-tts': 'eleven_multilingual_v2', |
| 'minimax-tts': 'speech-2.8-hd', |
| 'browser-native-tts': '', |
| }; |
|
|
| |
| |
| |
| export function getAllTTSProviders( |
| customProviders?: Record<string, TTSProviderConfig>, |
| ): TTSProviderConfig[] { |
| const builtIn = Object.values(TTS_PROVIDERS); |
| const custom = customProviders ? Object.values(customProviders) : []; |
| return [...builtIn, ...custom]; |
| } |
|
|
| |
| |
| |
| export function getTTSProvider( |
| providerId: TTSProviderId, |
| customProviders?: Record<string, TTSProviderConfig>, |
| ): TTSProviderConfig | undefined { |
| if (providerId in TTS_PROVIDERS) { |
| return TTS_PROVIDERS[providerId as BuiltInTTSProviderId]; |
| } |
| return customProviders?.[providerId]; |
| } |
|
|
| |
| |
| |
| export function getTTSVoices( |
| providerId: TTSProviderId, |
| customProviders?: Record<string, TTSProviderConfig>, |
| ): TTSVoiceInfo[] { |
| return getTTSProvider(providerId, customProviders)?.voices || []; |
| } |
|
|
| |
| |
| |
| export function getAllASRProviders( |
| customProviders?: Record<string, ASRProviderConfig>, |
| ): ASRProviderConfig[] { |
| const builtIn = Object.values(ASR_PROVIDERS); |
| const custom = customProviders ? Object.values(customProviders) : []; |
| return [...builtIn, ...custom]; |
| } |
|
|
| |
| |
| |
| export function getASRProvider( |
| providerId: ASRProviderId, |
| customProviders?: Record<string, ASRProviderConfig>, |
| ): ASRProviderConfig | undefined { |
| if (providerId in ASR_PROVIDERS) { |
| return ASR_PROVIDERS[providerId as BuiltInASRProviderId]; |
| } |
| return customProviders?.[providerId]; |
| } |
|
|
| |
| |
| |
| export function getASRSupportedLanguages( |
| providerId: ASRProviderId, |
| customProviders?: Record<string, ASRProviderConfig>, |
| ): string[] { |
| return getASRProvider(providerId, customProviders)?.supportedLanguages || []; |
| } |
|
|