feat: add MiMo TTS provider 语音TTS提供接入MiMo (#752)

* feat: add MiMo TTS provider with preset voices, voice design and voice clone

* refactor: remove MiMo voice clone feature
This commit is contained in:
ZhangKai | 张凯
2026-05-16 08:55:23 +08:00
committed by GitHub
parent 3f8461d9eb
commit 87a8e95d66
13 changed files with 609 additions and 11 deletions
@@ -371,22 +371,22 @@ const canPlaySpeech = computed(() => {
// 只有 assistant 消息可以播放
if (props.message.role !== 'assistant') return false
if (!copyableContent.value) return false
// OpenAI / Custom / Edge 不依赖浏览器 Web Speech API
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') return true
// OpenAI / Custom / Edge / MiMo 不依赖浏览器 Web Speech API
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') return true
return speech.isSupported
})
const isPlayingThisMessage = computed(() => {
// OpenAI / Custom / Edge 模式
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') {
// OpenAI / Custom / Edge / MiMo 模式
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') {
return speech.currentCustomMessageId.value === props.message.id && speech.isCustomPlaying.value
}
return speech.currentMessageId.value === props.message.id && speech.isPlaying.value
})
const isPausedThisMessage = computed(() => {
// OpenAI / Custom / Edge 模式
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') {
// OpenAI / Custom / Edge / MiMo 模式
if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') {
return speech.currentCustomMessageId.value === props.message.id && speech.isCustomPaused.value
}
return speech.currentMessageId.value === props.message.id && speech.isPaused.value
@@ -441,6 +441,24 @@ function handleSpeechToggle() {
return
}
// MiMo TTS 模式
if (voiceSettings.provider.value === 'mimo') {
const apiKey = voiceSettings.mimoApiKey.value
if (!apiKey) {
console.warn('[MessageItem] MiMo TTS API Key 为空')
return
}
speech.mimoToggle(props.message.id, content, {
baseUrl: voiceSettings.mimoBaseUrl.value,
apiKey,
model: voiceSettings.mimoModel.value,
voice: voiceSettings.mimoVoice.value,
voiceDesignDesc: voiceSettings.mimoVoiceDesignDesc.value || undefined,
stylePrompt: voiceSettings.mimoStylePrompt.value || undefined,
})
return
}
// Web Speech API 模式
if (voiceSettings.provider.value === 'webspeech') {
const text = speech.extractReadableText(content)
@@ -486,6 +504,18 @@ onMounted(() => {
rate: speedToEdgeRate(voiceSettings.edgeRate.value),
pitch: hzToEdgePitch(voiceSettings.edgePitchHz.value),
})
} else if (voiceSettings.provider.value === 'mimo') {
const apiKey = voiceSettings.mimoApiKey.value
if (apiKey) {
speech.mimoPlay(props.message.id, content, {
baseUrl: voiceSettings.mimoBaseUrl.value,
apiKey,
model: voiceSettings.mimoModel.value,
voice: voiceSettings.mimoVoice.value,
voiceDesignDesc: voiceSettings.mimoVoiceDesignDesc.value || undefined,
stylePrompt: voiceSettings.mimoStylePrompt.value || undefined,
})
}
} else if (voiceSettings.provider.value === 'webspeech') {
const text = speech.extractReadableText(content)
if (text) {
@@ -19,6 +19,7 @@ const providerOptions = [
{ label: t('settings.voice.providerOpenai'), value: 'openai' },
{ label: t('settings.voice.providerCustom'), value: 'custom' },
{ label: t('settings.voice.providerEdge'), value: 'edge' },
{ label: t('settings.voice.providerMimo'), value: 'mimo' },
]
const openaiModelOptions = [
@@ -76,6 +77,28 @@ onMounted(() => {
}
})
// ── MiMo TTS options ──
const mimoBaseUrlOptions = [
{ label: 'https://api.xiaomimimo.com/v1', value: 'https://api.xiaomimimo.com/v1' },
{ label: 'https://token-plan-cn.xiaomimimo.com/v1', value: 'https://token-plan-cn.xiaomimimo.com/v1' },
]
const mimoModelOptions = [
{ label: t('settings.voice.mimoModelPreset'), value: 'mimo-v2.5-tts' },
{ label: t('settings.voice.mimoModelVoiceDesign'), value: 'mimo-v2.5-tts-voicedesign' },
]
const mimoVoiceOptions = [
{ label: '冰糖 (中文·女)', value: '冰糖' },
{ label: '茉莉 (中文·女)', value: '茉莉' },
{ label: '苏打 (中文·男)', value: '苏打' },
{ label: '白桦 (中文·男)', value: '白桦' },
{ label: 'Mia (English·Female)', value: 'Mia' },
{ label: 'Chloe (English·Female)', value: 'Chloe' },
{ label: 'Milo (English·Male)', value: 'Milo' },
{ label: 'Dean (English·Male)', value: 'Dean' },
]
async function handleTest() {
const text = testText.value.trim()
if (!text) return
@@ -113,6 +136,19 @@ async function handleTest() {
rate: speedToEdgeRate(vs.edgeRate.value),
pitch: hzToEdgePitch(vs.edgePitchHz.value),
})
} else if (vs.provider.value === 'mimo') {
if (!vs.mimoApiKey.value) {
console.warn('[VoiceSettings] MiMo API Key empty')
return
}
await speech.mimoPlay('__test__', text, {
baseUrl: vs.mimoBaseUrl.value,
apiKey: vs.mimoApiKey.value,
model: vs.mimoModel.value,
voice: vs.mimoVoice.value,
voiceDesignDesc: vs.mimoVoiceDesignDesc.value || undefined,
stylePrompt: vs.mimoStylePrompt.value || undefined,
})
}
} catch (err) {
console.error('[VoiceSettings] Test failed:', err)
@@ -312,6 +348,104 @@ async function handleTest() {
</template>
<!-- MiMo TTS -->
<template v-if="vs.provider.value === 'mimo'">
<div class="provider-hint">
{{ t('settings.voice.mimoHint') }}
</div>
<SettingRow
:label="t('settings.voice.mimoApiKey')"
:hint="t('settings.voice.mimoApiKeyHint')"
>
<NInput
:value="vs.mimoApiKey.value"
type="password"
size="small"
show-password-on="click"
style="width: 360px"
:placeholder="t('settings.voice.mimoApiKeyPlaceholder')"
@update:value="vs.setMimoApiKey"
/>
</SettingRow>
<SettingRow
:label="t('settings.voice.mimoBaseUrl')"
:hint="t('settings.voice.mimoBaseUrlHint')"
>
<NSelect
:value="vs.mimoBaseUrl.value"
:options="mimoBaseUrlOptions"
size="small"
filterable
tag
style="width: 360px"
@update:value="vs.setMimoBaseUrl"
/>
</SettingRow>
<SettingRow
:label="t('settings.voice.mimoModel')"
:hint="t('settings.voice.mimoModelHint')"
>
<NSelect
:value="vs.mimoModel.value"
:options="mimoModelOptions"
size="small"
style="width: 320px"
@update:value="vs.setMimoModel"
/>
</SettingRow>
<!-- Preset voice mode -->
<SettingRow
v-if="vs.mimoModel.value === 'mimo-v2.5-tts'"
:label="t('settings.voice.mimoVoice')"
:hint="t('settings.voice.mimoVoiceHint')"
>
<NSelect
:value="vs.mimoVoice.value"
:options="mimoVoiceOptions"
size="small"
style="width: 200px"
@update:value="vs.setMimoVoice"
/>
</SettingRow>
<!-- Voice design mode -->
<SettingRow
v-if="vs.mimoModel.value === 'mimo-v2.5-tts-voicedesign'"
:label="t('settings.voice.mimoVoiceDesignPrompt')"
:hint="t('settings.voice.mimoVoiceDesignPromptHint')"
>
<NInput
:value="vs.mimoVoiceDesignDesc.value"
type="textarea"
size="small"
style="width: 360px"
:rows="3"
:placeholder="t('settings.voice.mimoVoiceDesignPromptPlaceholder')"
@update:value="vs.setMimoVoiceDesignDesc"
/>
</SettingRow>
<!-- Style prompt (available for all models) -->
<SettingRow
:label="t('settings.voice.mimoStylePrompt')"
:hint="t('settings.voice.mimoStylePromptHint')"
>
<NInput
:value="vs.mimoStylePrompt.value"
type="textarea"
size="small"
style="width: 360px"
:rows="2"
:placeholder="t('settings.voice.mimoStylePromptPlaceholder')"
@update:value="vs.setMimoStylePrompt"
/>
</SettingRow>
</template>
<!-- Test / Audition -->
<div class="test-section">
<h4 class="test-title">{{ t('settings.voice.testTitle') }}</h4>
+155 -3
View File
@@ -15,6 +15,15 @@ export interface OpenaiTtsOptions {
pitch?: string // Edge TTS pitch format, e.g. "-8Hz"
}
export interface MimoTtsOptions {
baseUrl: string
apiKey: string
model: string
voice: string // preset voice ID (preset mode) or data URI (clone mode)
voiceDesignDesc?: string // voice design description text (voice design mode)
stylePrompt?: string // natural language style instruction
}
export interface SpeechState {
isPlaying: boolean
isPaused: boolean
@@ -333,20 +342,17 @@ export function useSpeech() {
function openaiToggle(messageId: string, content: string, opts: OpenaiTtsOptions) {
if (currentCustomMessageId.value === messageId && isCustomPlaying.value) {
if (isCustomPaused.value) {
// Resume
if (customAudio) {
customAudio.play()
}
isCustomPaused.value = false
} else {
// Pause
if (customAudio) {
customAudio.pause()
}
isCustomPaused.value = true
}
} else {
// Stop other speech and start new
stop(false)
if (customAudio) {
customAudio.pause()
@@ -356,6 +362,148 @@ export function useSpeech() {
}
}
// ─── MiMo TTS Engine ──────────────────────────────────────────
async function mimoPlay(
messageId: string,
content: string,
opts: MimoTtsOptions,
) {
const text = extractReadableText(content)
if (!text) return
const token = ++playbackToken
isCustomPlaying.value = true
isCustomPaused.value = false
currentCustomMessageId.value = messageId
// Build messages based on model type
const messages: Array<{ role: string; content: string }> = []
if (opts.model === 'mimo-v2.5-tts-voicedesign') {
// Voice design: user message = voice description (+ appended style prompt)
const desc = opts.voiceDesignDesc || ''
const userContent = opts.stylePrompt
? `${desc}\n风格指令:${opts.stylePrompt}`
: desc
messages.push({ role: 'user', content: userContent || '默认音色' })
} else {
// Preset voices: user message = style prompt or empty
messages.push({ role: 'user', content: opts.stylePrompt || '' })
}
// assistant message = synthesis text
messages.push({ role: 'assistant', content: text })
const audio: Record<string, any> = { format: 'wav' }
// Voice design model does not accept audio.voice
if (opts.model !== 'mimo-v2.5-tts-voicedesign') {
audio.voice = opts.voice
}
const body: Record<string, any> = {
model: opts.model,
messages,
audio,
}
const url = `${opts.baseUrl.replace(/\/+$/, '')}/chat/completions`
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'api-key': opts.apiKey,
}
try {
const res = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(body),
})
if (token !== playbackToken) return
if (!res.ok) {
const errText = await res.text().catch(() => '')
throw new Error(`MiMo TTS 返回 ${res.status}: ${errText || res.statusText}`)
}
const json = await res.json()
if (token !== playbackToken) return
const audioBase64 = json?.choices?.[0]?.message?.audio?.data
if (!audioBase64) {
throw new Error('MiMo TTS 响应中未找到音频数据')
}
// base64 → binary → Blob
const binaryStr = atob(audioBase64)
const bytes = new Uint8Array(binaryStr.length)
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i)
}
const audioBlob = new Blob([bytes], { type: 'audio/wav' })
if (token !== playbackToken) return
const audioUrl = URL.createObjectURL(audioBlob)
const audio = new Audio(audioUrl)
customAudio = audio
audio.onended = () => {
if (token !== playbackToken) return
URL.revokeObjectURL(audioUrl)
isCustomPlaying.value = false
isCustomPaused.value = false
currentCustomMessageId.value = null
customAudio = null
}
audio.onerror = () => {
if (token !== playbackToken) return
URL.revokeObjectURL(audioUrl)
console.warn('[useSpeech] MiMo TTS audio playback error')
isCustomPlaying.value = false
isCustomPaused.value = false
currentCustomMessageId.value = null
customAudio = null
}
await audio.play()
} catch (err) {
if (token !== playbackToken) return
console.error('[useSpeech] MiMo TTS 请求失败:', err)
isCustomPlaying.value = false
isCustomPaused.value = false
currentCustomMessageId.value = null
throw err
}
}
function mimoToggle(messageId: string, content: string, opts: MimoTtsOptions) {
if (currentCustomMessageId.value === messageId && isCustomPlaying.value) {
if (isCustomPaused.value) {
if (customAudio) {
customAudio.play()
}
isCustomPaused.value = false
} else {
if (customAudio) {
customAudio.pause()
}
isCustomPaused.value = true
}
} else {
stop(false)
if (customAudio) {
customAudio.pause()
customAudio = null
}
mimoPlay(messageId, content, opts)
}
}
// ─── Unified speak ──────────────────────────────────────────
function speak(messageId: string, text: string, options: SpeechOptions = {}) {
@@ -473,6 +621,10 @@ export function useSpeech() {
openaiPlay,
openaiToggle,
// MiMo TTS
mimoPlay,
mimoToggle,
// Browser WebSpeech (直接调用避免 Rolldown 树摇)
speakViaBrowser,
}
@@ -1,6 +1,6 @@
import { ref, watch } from 'vue'
export type TtsProvider = 'webspeech' | 'openai' | 'custom' | 'edge'
export type TtsProvider = 'webspeech' | 'openai' | 'custom' | 'edge' | 'mimo'
export interface VoiceSettingsData {
provider: TtsProvider
@@ -23,6 +23,14 @@ export interface VoiceSettingsData {
edgeVoice: string
edgeRate: number // 语速倍率 0.5~2.01.0 = 正常
edgePitchHz: number // 音调偏移 Hz-20~200 = 正常
// MiMo TTS
mimoApiKey: string
mimoBaseUrl: string
mimoModel: string // 'mimo-v2.5-tts' | 'mimo-v2.5-tts-voicedesign'
mimoVoice: string // 预置音色 ID
mimoVoiceDesignDesc: string // 音色设计描述文本
mimoStylePrompt: string // 风格指令
}
const STORAGE_KEY = 'hermes-tts-settings-v2'
@@ -67,6 +75,13 @@ const DEFAULT: VoiceSettingsData = {
edgeVoice: 'zh-CN-XiaoxiaoNeural',
edgeRate: 1.0,
edgePitchHz: 0,
mimoApiKey: '',
mimoBaseUrl: 'https://api.xiaomimimo.com/v1',
mimoModel: 'mimo-v2.5-tts',
mimoVoice: '冰糖',
mimoVoiceDesignDesc: '',
mimoStylePrompt: '',
}
function sanitize(data: VoiceSettingsData): VoiceSettingsData {
@@ -110,10 +125,19 @@ const edgeVoice = ref<string>(load().edgeVoice)
const edgeRate = ref<number>(load().edgeRate)
const edgePitchHz = ref<number>(load().edgePitchHz)
// MiMo TTS
const mimoApiKey = ref<string>(load().mimoApiKey)
const mimoBaseUrl = ref<string>(load().mimoBaseUrl)
const mimoModel = ref<string>(load().mimoModel)
const mimoVoice = ref<string>(load().mimoVoice)
const mimoVoiceDesignDesc = ref<string>(load().mimoVoiceDesignDesc)
const mimoStylePrompt = ref<string>(load().mimoStylePrompt)
// Auto-persist on change
watch(
[provider, webspeechVoice, openaiApiKey, openaiBaseUrl, openaiModel, openaiVoice,
customUrl, customApiKey, edgeUrl, edgeVoice, edgeRate, edgePitchHz],
customUrl, customApiKey, edgeUrl, edgeVoice, edgeRate, edgePitchHz,
mimoApiKey, mimoBaseUrl, mimoModel, mimoVoice, mimoVoiceDesignDesc, mimoStylePrompt],
() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify({
provider: provider.value,
@@ -128,6 +152,12 @@ watch(
edgeVoice: edgeVoice.value,
edgeRate: edgeRate.value,
edgePitchHz: edgePitchHz.value,
mimoApiKey: mimoApiKey.value,
mimoBaseUrl: mimoBaseUrl.value,
mimoModel: mimoModel.value,
mimoVoice: mimoVoice.value,
mimoVoiceDesignDesc: mimoVoiceDesignDesc.value,
mimoStylePrompt: mimoStylePrompt.value,
}))
},
)
@@ -146,6 +176,12 @@ export function useVoiceSettings() {
edgeVoice,
edgeRate,
edgePitchHz,
mimoApiKey,
mimoBaseUrl,
mimoModel,
mimoVoice,
mimoVoiceDesignDesc,
mimoStylePrompt,
setProvider(v: TtsProvider) { provider.value = v },
setWebSpeechVoice(v: string) { webspeechVoice.value = v },
@@ -159,6 +195,12 @@ export function useVoiceSettings() {
setEdgeVoice(v: string) { edgeVoice.value = v },
setEdgeRate(v: number) { edgeRate.value = v },
setEdgePitchHz(v: number) { edgePitchHz.value = v },
setMimoApiKey(v: string) { mimoApiKey.value = v },
setMimoBaseUrl(v: string) { mimoBaseUrl.value = v },
setMimoModel(v: string) { mimoModel.value = v },
setMimoVoice(v: string) { mimoVoice.value = v },
setMimoVoiceDesignDesc(v: string) { mimoVoiceDesignDesc.value = v },
setMimoStylePrompt(v: string) { mimoStylePrompt.value = v },
reset() {
provider.value = DEFAULT.provider
@@ -173,6 +215,12 @@ export function useVoiceSettings() {
edgeVoice.value = DEFAULT.edgeVoice
edgeRate.value = DEFAULT.edgeRate
edgePitchHz.value = DEFAULT.edgePitchHz
mimoApiKey.value = DEFAULT.mimoApiKey
mimoBaseUrl.value = DEFAULT.mimoBaseUrl
mimoModel.value = DEFAULT.mimoModel
mimoVoice.value = DEFAULT.mimoVoice
mimoVoiceDesignDesc.value = DEFAULT.mimoVoiceDesignDesc
mimoStylePrompt.value = DEFAULT.mimoStylePrompt
},
}
}
+26
View File
@@ -670,6 +670,32 @@ jobTriggered: 'Job ausgelost',
testButton: 'Testen',
testButtonPlaying: 'Wiedergabe...',
testFailed: 'Test fehlgeschlagen: {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — unterstützt Voreingestellte Stimmen, Stimmdesign und Stimmklonung',
mimoApiKey: 'API-Schluessel',
mimoApiKeyHint: 'Holen Sie sich Ihren Schluessel auf platform.xiaomimimo.com',
mimoApiKeyPlaceholder: 'MiMo API-Schluessel',
mimoBaseUrl: 'Basis-URL',
mimoBaseUrlHint: 'MiMo API-Endpunkt-URL',
mimoModel: 'Modell',
mimoModelHint: 'Sprachsynthesemodell auswählen',
mimoModelPreset: 'Voreingestellte Stimmen',
mimoModelVoiceDesign: 'Stimmdesign',
mimoModelVoiceClone: 'Stimmklonung',
mimoVoice: 'Stimme',
mimoVoiceHint: 'Voreingestellte Stimme auswählen',
mimoVoiceDesignPrompt: 'Stimmbeschreibung',
mimoVoiceDesignPromptHint: 'Beschreiben Sie die gewünschten Stimmmerkmale',
mimoVoiceDesignPromptPlaceholder: 'Z.B.: Eine warme junge Frauenstimme, etwas langsam, mit magnetischem Ton',
mimoCloneAudio: 'Audio hochladen',
mimoCloneAudioHint: 'Audio-Beispiel für Stimmklonung hochladen (mp3/wav, max. 10 MB)',
mimoCloneAudioUpload: 'Datei auswählen',
mimoCloneAudioClear: 'Löschen',
mimoStylePrompt: 'Stil-Eingabe',
mimoStylePromptHint: 'Optional — beschreiben Sie den Sprechstil in natürlicher Sprache',
mimoStylePromptPlaceholder: 'Z.B.: Heller, lebhafter Ton, schnelles Tempo',
},
lockedIps: {
title: 'Gesperrte IPs',
+26
View File
@@ -847,6 +847,32 @@ export default {
testButton: 'Test',
testButtonPlaying: 'Playing...',
testFailed: 'Test failed: {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — supports preset voices, voice design, and voice clone modes',
mimoApiKey: 'API Key',
mimoApiKeyHint: 'Get your key at platform.xiaomimimo.com',
mimoApiKeyPlaceholder: 'MiMo API Key',
mimoBaseUrl: 'Base URL',
mimoBaseUrlHint: 'MiMo API endpoint URL',
mimoModel: 'Model',
mimoModelHint: 'Select speech synthesis model',
mimoModelPreset: 'Preset Voices',
mimoModelVoiceDesign: 'Voice Design',
mimoModelVoiceClone: 'Voice Clone',
mimoVoice: 'Voice',
mimoVoiceHint: 'Select a preset voice',
mimoVoiceDesignPrompt: 'Voice Description',
mimoVoiceDesignPromptHint: 'Describe the voice characteristics you want',
mimoVoiceDesignPromptPlaceholder: 'e.g., A warm young female voice, slightly slow, with a magnetic tone',
mimoCloneAudio: 'Upload Audio',
mimoCloneAudioHint: 'Upload an audio sample for voice cloning (mp3/wav, max 10MB)',
mimoCloneAudioUpload: 'Choose File',
mimoCloneAudioClear: 'Clear',
mimoStylePrompt: 'Style Prompt',
mimoStylePromptHint: 'Optional — describe the speaking style in natural language',
mimoStylePromptPlaceholder: 'e.g., Bright and bouncy tone, fast pace',
},
},
+26
View File
@@ -670,6 +670,32 @@ jobTriggered: 'Job ejecutado',
testButton: 'Probar',
testButtonPlaying: 'Reproduciendo...',
testFailed: 'Prueba fallida: {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — voces predefinidas, diseño de voz y clonación de voz',
mimoApiKey: 'Clave API',
mimoApiKeyHint: 'Obtenga su clave en platform.xiaomimimo.com',
mimoApiKeyPlaceholder: 'Clave API MiMo',
mimoBaseUrl: 'URL base',
mimoBaseUrlHint: 'URL del endpoint de la API MiMo',
mimoModel: 'Modelo',
mimoModelHint: 'Seleccione el modelo de síntesis de voz',
mimoModelPreset: 'Voces predefinidas',
mimoModelVoiceDesign: 'Diseño de voz',
mimoModelVoiceClone: 'Clonación de voz',
mimoVoice: 'Voz',
mimoVoiceHint: 'Seleccione una voz predefinida',
mimoVoiceDesignPrompt: 'Descripción de voz',
mimoVoiceDesignPromptHint: 'Describa las características de voz deseadas',
mimoVoiceDesignPromptPlaceholder: 'Ej: Una voz femenina cálida y joven, algo lenta, con tono magnético',
mimoCloneAudio: 'Subir audio',
mimoCloneAudioHint: 'Suba una muestra de audio para clonación (mp3/wav, máx. 10 MB)',
mimoCloneAudioUpload: 'Elegir archivo',
mimoCloneAudioClear: 'Borrar',
mimoStylePrompt: 'Indicador de estilo',
mimoStylePromptHint: 'Opcional — describa el estilo de habla en lenguaje natural',
mimoStylePromptPlaceholder: 'Ej: Tono brillante y animado, ritmo rápido',
},
lockedIps: {
title: 'IPs bloqueadas',
+26
View File
@@ -670,6 +670,32 @@ jobTriggered: 'Job declenche',
testButton: 'Tester',
testButtonPlaying: 'Lecture...',
testFailed: 'Echec du test : {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — voices predefinies, conception vocale et clonage vocal',
mimoApiKey: 'Cle API',
mimoApiKeyHint: 'Obtenez votre cle sur platform.xiaomimimo.com',
mimoApiKeyPlaceholder: 'Cle API MiMo',
mimoBaseUrl: 'URL de base',
mimoBaseUrlHint: 'URL de l\'endpoint API MiMo',
mimoModel: 'Modele',
mimoModelHint: 'Selectionnez le modele de synthese vocale',
mimoModelPreset: 'Voix predefinies',
mimoModelVoiceDesign: 'Conception vocale',
mimoModelVoiceClone: 'Clonage vocal',
mimoVoice: 'Voix',
mimoVoiceHint: 'Selectionnez une voix predefinie',
mimoVoiceDesignPrompt: 'Description vocale',
mimoVoiceDesignPromptHint: 'Decrivez les caracteristiques vocales souhaitees',
mimoVoiceDesignPromptPlaceholder: 'Ex : Une voix feminine chaude et jeune, legerement lente, avec un ton magnetique',
mimoCloneAudio: 'Televerser un audio',
mimoCloneAudioHint: 'Televersez un echantillon audio pour le clonage (mp3/wav, max 10 Mo)',
mimoCloneAudioUpload: 'Choisir un fichier',
mimoCloneAudioClear: 'Effacer',
mimoStylePrompt: 'Invite de style',
mimoStylePromptHint: 'Optionnel — decrivez le style de parole en langage naturel',
mimoStylePromptPlaceholder: 'Ex : Ton vif et entrain, rythme rapide',
},
lockedIps: {
title: 'IPs bloquees',
+26
View File
@@ -670,6 +670,32 @@ export default {
testButton: 'テスト',
testButtonPlaying: '再生中...',
testFailed: 'テスト失敗:{error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — プリセット音声、音声デザイン、音声クローンの3つのモードをサポート',
mimoApiKey: 'API Key',
mimoApiKeyHint: 'platform.xiaomimimo.com で取得',
mimoApiKeyPlaceholder: 'MiMo API Key',
mimoBaseUrl: 'Base URL',
mimoBaseUrlHint: 'MiMo API エンドポイントURL',
mimoModel: 'モデル',
mimoModelHint: '音声合成モデルを選択',
mimoModelPreset: 'プリセット音声',
mimoModelVoiceDesign: '音声デザイン',
mimoModelVoiceClone: '音声クローン',
mimoVoice: '音声',
mimoVoiceHint: 'プリセット音声を選択',
mimoVoiceDesignPrompt: '音声の説明',
mimoVoiceDesignPromptHint: '希望する音声の特徴を説明してください',
mimoVoiceDesignPromptPlaceholder: '例:温かみのある若い女性の声、少しゆっくり、磁力的なトーン',
mimoCloneAudio: '音声アップロード',
mimoCloneAudioHint: '音声クローン用の音声サンプルをアップロード(mp3/wav、最大10MB',
mimoCloneAudioUpload: 'ファイルを選択',
mimoCloneAudioClear: 'クリア',
mimoStylePrompt: 'スタイルプロンプト',
mimoStylePromptHint: 'オプション — 自然言語で話すスタイルを説明',
mimoStylePromptPlaceholder: '例:明るく弾むようなトーン、速めのテンポ',
},
lockedIps: {
title: 'ロック済みIP管理',
+26
View File
@@ -670,6 +670,32 @@ export default {
testButton: '테스트',
testButtonPlaying: '재생 중...',
testFailed: '테스트 실패: {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: '샤오미 MiMo TTS — 프리셋 음성, 음성 디자인, 음성 클론 세 가지 모드 지원',
mimoApiKey: 'API Key',
mimoApiKeyHint: 'platform.xiaomimimo.com에서 발급',
mimoApiKeyPlaceholder: 'MiMo API Key',
mimoBaseUrl: 'Base URL',
mimoBaseUrlHint: 'MiMo API 엔드포인트 URL',
mimoModel: '모델',
mimoModelHint: '음성 합성 모델 선택',
mimoModelPreset: '프리셋 음성',
mimoModelVoiceDesign: '음성 디자인',
mimoModelVoiceClone: '음성 클론',
mimoVoice: '음성',
mimoVoiceHint: '프리셋 음성 선택',
mimoVoiceDesignPrompt: '음성 설명',
mimoVoiceDesignPromptHint: '원하는 음성 특징을 설명하세요',
mimoVoiceDesignPromptPlaceholder: '예: 따뜻한 젊은 여성 목소리, 약간 느린 속도, 마그네틱한 톤',
mimoCloneAudio: '오디오 업로드',
mimoCloneAudioHint: '음성 클론용 오디오 샘플 업로드 (mp3/wav, 최대 10MB)',
mimoCloneAudioUpload: '파일 선택',
mimoCloneAudioClear: '지우기',
mimoStylePrompt: '스타일 프롬프트',
mimoStylePromptHint: '선택사항 — 자연어로 말하기 스타일 설명',
mimoStylePromptPlaceholder: '예: 밝고 경쾌한 톤, 빠른 속도',
},
lockedIps: {
title: '잠긴 IP 관리',
+26
View File
@@ -670,6 +670,32 @@ jobTriggered: 'Job acionado',
testButton: 'Testar',
testButtonPlaying: 'Reproduzindo...',
testFailed: 'Teste falhou: {error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: 'Xiaomi MiMo TTS — vozes predefinidas, design de voz e clonagem de voz',
mimoApiKey: 'Chave API',
mimoApiKeyHint: 'Obtenha sua chave em platform.xiaomimimo.com',
mimoApiKeyPlaceholder: 'Chave API MiMo',
mimoBaseUrl: 'URL base',
mimoBaseUrlHint: 'URL do endpoint da API MiMo',
mimoModel: 'Modelo',
mimoModelHint: 'Selecione o modelo de síntese de voz',
mimoModelPreset: 'Vozes predefinidas',
mimoModelVoiceDesign: 'Design de voz',
mimoModelVoiceClone: 'Clonagem de voz',
mimoVoice: 'Voz',
mimoVoiceHint: 'Selecione uma voz predefinida',
mimoVoiceDesignPrompt: 'Descrição da voz',
mimoVoiceDesignPromptHint: 'Descreva as características de voz desejadas',
mimoVoiceDesignPromptPlaceholder: 'Ex: Uma voz feminina quente e jovem, ligeiramente lenta, com tom magnético',
mimoCloneAudio: 'Enviar áudio',
mimoCloneAudioHint: 'Envie uma amostra de áudio para clonagem (mp3/wav, máx. 10 MB)',
mimoCloneAudioUpload: 'Escolher arquivo',
mimoCloneAudioClear: 'Limpar',
mimoStylePrompt: 'Prompt de estilo',
mimoStylePromptHint: 'Opcional — descreva o estilo de fala em linguagem natural',
mimoStylePromptPlaceholder: 'Ex: Tom brilhante e animado, ritmo rápido',
},
lockedIps: {
title: 'IPs bloqueadas',
+26
View File
@@ -836,6 +836,32 @@ export default {
testButton: '試聽',
testButtonPlaying: '播放中...',
testFailed: '測試失敗:{error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: '小米 MiMo TTS,支援預設音色、音色設計、音色複製三種模式',
mimoApiKey: 'API Key',
mimoApiKeyHint: '在 platform.xiaomimimo.com 取得',
mimoApiKeyPlaceholder: 'MiMo API Key',
mimoBaseUrl: 'Base URL',
mimoBaseUrlHint: 'MiMo API 端點位址',
mimoModel: '模型',
mimoModelHint: '選擇語音合成模型',
mimoModelPreset: '預設音色',
mimoModelVoiceDesign: '音色設計',
mimoModelVoiceClone: '音色複製',
mimoVoice: '音色',
mimoVoiceHint: '選擇預設音色',
mimoVoiceDesignPrompt: '音色描述',
mimoVoiceDesignPromptHint: '描述你想要的音色特徵',
mimoVoiceDesignPromptPlaceholder: '例如:溫柔的年輕女聲,語速稍慢,帶著磁性',
mimoCloneAudio: '上傳音訊',
mimoCloneAudioHint: '上傳音訊樣本用於音色複製,支援 mp3/wav,最大 10MB',
mimoCloneAudioUpload: '選擇檔案',
mimoCloneAudioClear: '清除音訊',
mimoStylePrompt: '風格指令',
mimoStylePromptHint: '可選,用自然語言描述語音風格',
mimoStylePromptPlaceholder: '例如:用輕快上揚的語調,語速稍快',
},
},
+26
View File
@@ -839,6 +839,32 @@ export default {
testButton: '试听',
testButtonPlaying: '播放中...',
testFailed: '测试失败:{error}',
// MiMo TTS
providerMimo: 'MiMo TTS',
mimoHint: '小米 MiMo TTS,支持预置音色、音色设计、音色复刻三种模式',
mimoApiKey: 'API Key',
mimoApiKeyHint: '在 platform.xiaomimimo.com 获取',
mimoApiKeyPlaceholder: 'MiMo API Key',
mimoBaseUrl: 'Base URL',
mimoBaseUrlHint: 'MiMo API 端点地址',
mimoModel: '模型',
mimoModelHint: '选择语音合成模型',
mimoModelPreset: '预置音色',
mimoModelVoiceDesign: '音色设计',
mimoModelVoiceClone: '音色复刻',
mimoVoice: '音色',
mimoVoiceHint: '选择预置音色',
mimoVoiceDesignPrompt: '音色描述',
mimoVoiceDesignPromptHint: '描述你想要的音色特征',
mimoVoiceDesignPromptPlaceholder: '例如:温柔的年轻女声,语速稍慢,带着磁性',
mimoCloneAudio: '上传音频',
mimoCloneAudioHint: '上传音频样本用于音色复刻,支持 mp3/wav,最大 10MB',
mimoCloneAudioUpload: '选择文件',
mimoCloneAudioClear: '清除音频',
mimoStylePrompt: '风格指令',
mimoStylePromptHint: '可选,用自然语言描述语音风格',
mimoStylePromptPlaceholder: '例如:用轻快上扬的语调,语速稍快',
},
},