fix(stage-web|stage-tamagotchi): speech

This commit is contained in:
Neko Ayaka
2025-03-20 00:28:22 +08:00
parent 6f76310b8c
commit 4579ca24eb
11 changed files with 275 additions and 160 deletions
@@ -47,18 +47,22 @@ settings:
chinese: 简体中文
english: English
title: Language
live2d:
change-model:
from-file: Load from File
from-file-select: Select
from-url: Load from URL
from-url-confirm: Load
from-url-placeholder: Enter Live2D model URL
title: Change Model
edit-motion-map:
title: Edit motion map
map-motions:
play: Play Motion
title: Map Motions
title: Live2D Settings
microphone: Microphone
models: Model
quit: Quit
viewer: Viewer
shortcuts:
title: Shortcuts
window:
move: Move the window
resize: Resize the window
debug: Toggle developer tools
press_keys: Press keys...
other: Other
pages:
models:
description: Live2D, VRM, etc.
@@ -69,10 +73,9 @@ settings:
sections:
section:
provider-model-selection:
title: Model
description: Select the suitable LLM provider for consciousness
collapse: Collapse
custom_model_placeholder: Enter custom model name...
description: Select the suitable LLM provider for consciousness
error: Error loading models
expand: Expand
loading: Loading available models...
@@ -89,6 +92,7 @@ settings:
show_less: Show less
show_more: Show more
subtitle: Select a model from the provider
title: Model
title: Consciousness
description: Thinking, vision, speech synthesis, gaming, etc.
gaming-factorio:
@@ -111,13 +115,49 @@ settings:
title: Discord
speech:
description: Speech synthesis
sections:
section:
playground:
buttons:
stop:
label: Stop
select-voice:
required: Please select a voice
provider-voice-selection:
custom_model_placeholder: Enter custom model name...
custom_voice_placeholder: Enter custom voice ID...
description: Select the suitable speech provider
no_models: No models available
no_models_description: No models were found for this provider
no_voices: No voices available
no_voices_description: No voices were found for this provider
search_models_placeholder: Search models...
search_models_results: Found {count} of {total} models
search_voices_placeholder: Search voices...
search_voices_results: Found {count} of {total} voices
show_less: Show less
show_more: Show more
title: Provider
voice-settings:
input-ssml:
placeholder: Enter SSML text...
use-ssml:
description: Enable to input raw SSML instead of plain text
label: Use Custom SSML
title: Speech
title: Modules
vision:
description: Vision
title: Vision
x:
description: X / Twitter browsing and usage
title: X / Twitter
providers:
common:
fields:
field:
api-key:
label: API Key Input
section:
advanced:
fields:
@@ -148,6 +188,20 @@ settings:
api-key:
placeholder: Input Cloudflare Account ID
title: Cloudflare Workers AI
common:
fields:
field:
pitch:
description: >-
Tune the pitch of synthesized speech (e.g., sharper or
coarser)
label: Pitch
speed:
description: Adjust the speed of speech
label: Speed
volume:
description: Adjust the volume of speech
label: Volume
deepseek:
description: deepseek.com
title: DeepSeek
@@ -193,6 +247,12 @@ settings:
fireworks:
description: fireworks.ai
title: Fireworks.ai
microsoft-speech:
fields:
field:
region:
description: Speech Service region
label: Region
mistral:
description: mistral.ai
title: Mistral
@@ -246,10 +306,10 @@ settings:
description: Traditional Japanese color palette
title: Japanese Colors
monet:
description: Impressionist palette inspired by Claude Monet\'s works
description: Impressionist palette inspired by Claude Monet's works
title: Monet Colors
morandi:
description: Soft, muted tones inspired by Giorgio Morandi\'s paintings
description: Soft, muted tones inspired by Giorgio Morandi's paintings
title: Morandi Colors
nordic:
description: Scandinavian minimalist color scheme
@@ -35,17 +35,20 @@ settings:
chinese: 简体中文
english: English
title: 语言
live2d:
change-model:
from-file: 从文件加载
from-file-select: 选择
from-url: 从 URL 加载
from-url-confirm: 加载
from-url-placeholder: 输入 Live2D 模型 URL
title: 更换模型
map-motions:
play: 播放动作
title: 映射动作
title: Live2D 设置
microphone: 麦克风
models: 模型
quit: 退出
viewer: 查看器
shortcuts:
title: 快捷键
window:
move: 移动窗口
resize: 调整窗口大小
debug: 切换开发者模式
press_keys: 按下快捷键...
other: 其他
pages:
models:
description: 切换角色的 Live2DVRM 模型
@@ -53,14 +56,12 @@ settings:
modules:
consciousness:
description: 人格设定、使用的模型等配置
title: 意识
sections:
section:
provider-model-selection:
title: 模型
description: 为意识选择合适的 LLM 提供商
collapse: 折叠
custom_model_placeholder: 输入自订模型名字
description: 为意识选择合适的 LLM 提供商
error: 获取出错啦
expand: 展开
loading: 获取模型中...
@@ -77,6 +78,8 @@ settings:
show_less: 显示更多
show_more: 收起
subtitle: 选择一个模型
title: 模型
title: 意识
description: 思维,视觉,言语综合,游戏等
gaming-factorio:
description: 一起玩 Factorio
@@ -98,19 +101,57 @@ settings:
title: Discord
speech:
description: 语音合成
sections:
section:
playground:
buttons:
stop:
label: 停止
select-voice:
required: 请选择声线
provider-voice-selection:
custom_model_placeholder: 输入指定模型名字...
custom_voice_placeholder: 输入指定声线 ID...
description: 选择合适的语音提供商
no_models: 没有任何可用的模型
no_models_description: 该提供商的模型列表没有返回任何的数据
no_voices: 没有任何可用的声线
no_voices_description: 该提供商的声线列表没有返回任何的数据
pause: Pause
play_sample: Play
search_models_placeholder: 搜索模型...
search_models_results: 找到 {count} / {total} 个模型
search_voices_placeholder: 搜索声线...
search_voices_results: 找到 {count} / {total} 个声线
show_less: 显示更少
show_more: 显示更多
title: 选择语音合成提供商
voice-settings:
input-ssml:
placeholder: 请输入 SSML 文本
use-ssml:
description: 启用后可以输入原始 SSML 而不是纯文本
label: 选用自定义 SSML
title: 发声
title: 机体模块
vision:
description: 视觉
title: 视觉
x:
description: X / Twitter 的浏览和使用
title: X / Twitter
providers:
common:
fields:
field:
api-key:
label: API 密钥
section:
advanced:
fields:
field:
headers:
description: 添加自定义HTTP标头
description: 添加自定义 HTTP 标头
key:
placeholder: 请求头名字
label: HTTP 请求头
@@ -129,12 +170,24 @@ settings:
fields:
field:
account-id:
description: Cloudflare帐户ID
description: Cloudflare 帐户 ID
label: Cloudflare Account ID
placeholder: 请输入 Cloudflare Account ID
api-key:
placeholder: 请输入 Cloudflare Workers AI 的 API Key
title: Workers AI
common:
fields:
field:
pitch:
description: 调节合成语音的音高(比如更尖锐或更粗旷)
label: 音高
speed:
description: 调节说话的语速
label: 语速
volume:
description: 调节语音的响度
label: 响度
deepseek:
description: DeepSeek.com
title: 深度求索 DeepSeek
@@ -180,6 +233,12 @@ settings:
fireworks:
description: Fireworks.ai
title: Fireworks.ai
microsoft-speech:
fields:
field:
region:
description: 服务 Endpoint 地区(比如亚太 eastasia 区域)
label: Endpoint 地区
mistral:
description: mistral.ai
title: Mistral
@@ -231,7 +290,7 @@ settings:
monet:
title: 莫奈颜色
morandi:
title: Morandi颜色
title: Morandi 颜色
nordic:
title: 北欧颜色
title: 主题预设
@@ -29,6 +29,7 @@ const {
activeSpeechProvider,
activeSpeechModel,
activeSpeechVoice,
activeSpeechVoiceId,
pitch,
isLoadingSpeechProviderVoices,
supportsModelListing,
@@ -43,7 +44,6 @@ const {
const router = useRouter()
const voiceId = ref('')
const voiceSearchQuery = ref('')
const useSSML = ref(false)
const testText = ref('Hello, my name is AI Assistant')
@@ -100,7 +100,7 @@ async function generateTestSpeech() {
const input = useSSML.value
? ssmlText.value
: speechStore.generateSSML(testText.value, activeSpeechVoice.value)
: speechStore.generateSSML(testText.value, activeSpeechVoice.value, { ...providerConfig, pitch: pitch.value })
const response = await generateSpeech({
...provider.speech(activeSpeechModel.value, providerConfig),
@@ -148,10 +148,6 @@ onUnmounted(() => {
}
})
function handleVoiceSelection(value: string) {
activeSpeechVoice.value = availableVoices.value[activeSpeechProvider.value].find(voice => voice.id === value)
}
function updateCustomVoiceName(value: string) {
activeSpeechVoice.value = {
id: value,
@@ -167,16 +163,6 @@ function updateCustomVoiceName(value: string) {
function updateCustomModelName(value: string) {
activeSpeechModel.value = value
}
watch(voiceId, (newVoice) => {
const foundVoice = availableVoices.value[activeSpeechProvider.value].find(voice => voice.id === newVoice)
if (foundVoice) {
activeSpeechVoice.value = foundVoice
}
else {
updateCustomVoiceName(newVoice)
}
})
</script>
<template>
@@ -353,6 +339,7 @@ watch(voiceId, (newVoice) => {
>
<VoiceCardManySelect
v-model:search-query="voiceSearchQuery"
v-model:voice-id="activeSpeechVoiceId"
:voices="availableVoices[activeSpeechProvider]?.map(voice => ({
id: voice.id,
name: voice.name,
@@ -360,7 +347,6 @@ watch(voiceId, (newVoice) => {
previewURL: voice.previewURL,
customizable: false,
}))"
:selected-voice-id="activeSpeechVoice?.id"
:searchable="true"
:search-placeholder="t('settings.pages.modules.speech.sections.section.provider-voice-selection.search_voices_placeholder')"
:search-no-results-title="t('settings.pages.modules.speech.sections.section.provider-voice-selection.no_voices')"
@@ -371,7 +357,6 @@ watch(voiceId, (newVoice) => {
:collapse-button-text="t('settings.pages.modules.speech.sections.section.provider-voice-selection.show_less')"
:play-button-text="t('settings.pages.modules.speech.sections.section.provider-voice-selection.play_sample')"
:pause-button-text="t('settings.pages.modules.speech.sections.section.provider-voice-selection.pause')"
@update:selected-voice-id="handleVoiceSelection"
@update:custom-value="updateCustomVoiceName"
/>
</div>
@@ -423,7 +408,7 @@ watch(voiceId, (newVoice) => {
class="mt-2 space-y-6"
>
<FieldInput
v-model="voiceId"
v-model="activeSpeechVoiceId"
type="text"
label="Voice ID"
description="Enter the voice ID for your custom voice"
@@ -10,7 +10,7 @@ import {
} from '@proj-airi/stage-ui/components'
import { useProvidersStore, useSpeechStore } from '@proj-airi/stage-ui/stores'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
@@ -29,6 +29,10 @@ const speechStore = useSpeechStore()
const providersStore = useProvidersStore()
const { providers } = storeToRefs(providersStore)
const pitch = ref(0)
const speed = ref(1.0)
const volume = ref(0)
// Additional settings specific to Microsoft Speech (region)
const region = computed({
get: () => providers.value[providerId]?.region as string | undefined || 'eastasia',
@@ -80,6 +84,7 @@ async function handleGenerateSpeech(input: string, voiceId: string, useSSML: boo
const ssml = speechStore.generateSSML(
input,
voice,
{ ...providerConfig, pitch: pitch.value },
)
return await speechStore.speech(
provider,
@@ -123,6 +128,9 @@ async function handleGenerateSpeech(input: string, voiceId: string, useSSML: boo
<!-- Voice settings specific to Microsoft Speech -->
<template #voice-settings="{ voiceSettings, updateVoiceSettings }">
<SpeechVoiceSettings
v-model:pitch="pitch"
v-model:speed="speed"
v-model:volume="volume"
:settings="voiceSettings"
:show-pitch="true"
:show-speed="true"