feat: add Edge TTS rate/pitch sliders to voice settings (#629)
Add speed (rate) and pitch controls for Edge TTS provider: - Frontend: speedToEdgeRate()/hzToEdgePitch() helpers + UI sliders - Backend: rate/pitch passthrough in OpenaiTtsRequest and controller - i18n: add edgeRate/edgePitch keys across all 8 languages - Rate: 0.5x-2.0x slider, Pitch: -20Hz to +20Hz slider
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
} from "./highlight";
|
||||
import { useGlobalSpeech } from "@/composables/useSpeech";
|
||||
import { useVoiceSettings } from "@/composables/useVoiceSettings";
|
||||
import { speedToEdgeRate, hzToEdgePitch } from "@/utils/ttsHelpers";
|
||||
|
||||
const TOOL_PAYLOAD_DISPLAY_LIMIT = 2000;
|
||||
|
||||
@@ -420,6 +421,8 @@ function handleSpeechToggle() {
|
||||
speech.openaiToggle(props.message.id, content, {
|
||||
baseUrl: apiUrl,
|
||||
voice: voiceSettings.edgeVoice.value,
|
||||
rate: speedToEdgeRate(voiceSettings.edgeRate.value),
|
||||
pitch: hzToEdgePitch(voiceSettings.edgePitchHz.value),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -466,6 +469,8 @@ onMounted(() => {
|
||||
speech.openaiPlay(props.message.id, content, {
|
||||
baseUrl: '/api/tts/proxy',
|
||||
voice: voiceSettings.edgeVoice.value,
|
||||
rate: speedToEdgeRate(voiceSettings.edgeRate.value),
|
||||
pitch: hzToEdgePitch(voiceSettings.edgePitchHz.value),
|
||||
})
|
||||
} else if (voiceSettings.provider.value === 'webspeech') {
|
||||
const text = speech.extractReadableText(content)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { NSelect, NInput, NButton } from 'naive-ui'
|
||||
import { NSelect, NInput, NButton, NSlider } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useVoiceSettings } from '@/composables/useVoiceSettings'
|
||||
import { useSpeech } from '@/composables/useSpeech'
|
||||
import { speedToEdgeRate, hzToEdgePitch } from '@/utils/ttsHelpers'
|
||||
import SettingRow from './SettingRow.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -103,6 +104,8 @@ async function handleTest() {
|
||||
await speech.openaiPlay('__test__', text, {
|
||||
baseUrl: '/api/tts/proxy',
|
||||
voice: vs.edgeVoice.value,
|
||||
rate: speedToEdgeRate(vs.edgeRate.value),
|
||||
pitch: hzToEdgePitch(vs.edgePitchHz.value),
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -267,6 +270,40 @@ async function handleTest() {
|
||||
/>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
:label="t('settings.voice.edgeRate')"
|
||||
:hint="t('settings.voice.edgeRateHint')"
|
||||
>
|
||||
<div class="slider-row">
|
||||
<NSlider
|
||||
:value="vs.edgeRate.value"
|
||||
:min="0.5"
|
||||
:max="2.0"
|
||||
:step="0.05"
|
||||
style="width: 200px"
|
||||
@update:value="vs.setEdgeRate"
|
||||
/>
|
||||
<span class="slider-value">{{ vs.edgeRate.value.toFixed(2) }}x ({{ speedToEdgeRate(vs.edgeRate.value) }})</span>
|
||||
</div>
|
||||
</SettingRow>
|
||||
|
||||
<SettingRow
|
||||
:label="t('settings.voice.edgePitch')"
|
||||
:hint="t('settings.voice.edgePitchHint')"
|
||||
>
|
||||
<div class="slider-row">
|
||||
<NSlider
|
||||
:value="vs.edgePitchHz.value"
|
||||
:min="-20"
|
||||
:max="20"
|
||||
:step="1"
|
||||
style="width: 200px"
|
||||
@update:value="vs.setEdgePitchHz"
|
||||
/>
|
||||
<span class="slider-value">{{ vs.edgePitchHz.value > 0 ? '+' : '' }}{{ vs.edgePitchHz.value }} Hz ({{ hzToEdgePitch(vs.edgePitchHz.value) }})</span>
|
||||
</div>
|
||||
</SettingRow>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- ─── Test / Audition ─── -->
|
||||
@@ -324,4 +361,17 @@ async function handleTest() {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.slider-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.slider-value {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
min-width: 120px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user