语音合成

This commit is contained in:
2025-11-19 22:52:00 +08:00
parent 75abf48bc1
commit cc5401e743
6 changed files with 101 additions and 90 deletions

View File

@@ -137,6 +137,7 @@ const buildPreviewParams = (voice) => {
}
return {
voiceConfigId: configId,
inputText: ttsText.value, // 传递用户输入的文本
emotion: emotion.value || 'neutral',
speechRate: speechRate.value || 1.0,
audioFormat: 'mp3'
@@ -144,6 +145,7 @@ const buildPreviewParams = (voice) => {
} else {
return {
voiceId: voice.voiceId,
inputText: ttsText.value, // 传递用户输入的文本
emotion: emotion.value || 'neutral',
speechRate: speechRate.value || 1.0,
audioFormat: 'mp3'
@@ -209,16 +211,29 @@ const handleSynthesizeVoice = async () => {
const playSynthesizedAudio = () => {
// 防止重复点击
if (isPlayingSynthesized.value || !synthesizedAudio.value?.audioUrl) {
if (isPlayingSynthesized.value || !synthesizedAudio.value) {
return
}
isPlayingSynthesized.value = true
playAudioPreview(synthesizedAudio.value.audioUrl, {
onEnded: () => {
// 优先使用Base64数据安全方案
if (synthesizedAudio.value.audioBase64) {
playAudioFromBase64(synthesizedAudio.value.audioBase64, synthesizedAudio.value.format, () => {
isPlayingSynthesized.value = false
}
})
})
}
// 兼容旧的audioUrl方式已废弃
else if (synthesizedAudio.value.audioUrl) {
playAudioPreview(synthesizedAudio.value.audioUrl, {
onEnded: () => {
isPlayingSynthesized.value = false
}
})
} else {
message.warning('暂无可播放的音频')
isPlayingSynthesized.value = false
}
}
// 视频处理
@@ -329,7 +344,7 @@ const playAudioPreview = (url, options = {}) => {
})
}
const playAudioFromBase64 = (audioBase64, format = 'mp3') => {
const playAudioFromBase64 = (audioBase64, format = 'mp3', onEnded = null) => {
try {
previewObjectUrl && URL.revokeObjectURL(previewObjectUrl)
const byteCharacters = window.atob(audioBase64)
@@ -340,16 +355,18 @@ const playAudioFromBase64 = (audioBase64, format = 'mp3') => {
const mime = format === 'mp3' ? 'audio/mpeg' : `audio/${format}`
const blob = new Blob([new Uint8Array(byteNumbers)], { type: mime })
previewObjectUrl = URL.createObjectURL(blob)
playAudioPreview(previewObjectUrl, {
playAudioPreview(previewObjectUrl, {
revokeOnEnd: true,
onEnded: () => {
isPlayingPreview.value = false
onEnded && onEnded()
}
})
} catch (error) {
console.error('Base64播放失败:', error)
isPlayingPreview.value = false
message.error('音频播放失败')
onEnded && onEnded()
}
}
@@ -395,13 +412,13 @@ let previewObjectUrl = ''
<section class="digital-video-left">
<!-- 文本输入 -->
<div class="tts-section">
<div class="section-label">文案</div>
<a-textarea
v-model:value="ttsText"
placeholder="请输入你想让角色说话的内容"
:rows="6"
class="tts-textarea"
/>
<div class="tts-hint"> 试听后可获取准确的说话时长</div>
<!-- 音色选择 -->
<div class="voice-selection">
@@ -514,12 +531,12 @@ let previewObjectUrl = ''
<div v-if="synthesizedAudio" class="synth-audio-card">
<div class="synth-audio-title">已生成语音</div>
<div class="synth-audio-meta">
<span>文件编号{{ synthesizedAudio.fileId }}</span>
<span>格式{{ (synthesizedAudio.format || 'mp3').toUpperCase() }}</span>
<span v-if="synthesizedAudio.audioBase64">Base64编码</span>
</div>
<div class="synth-audio-actions">
<a-button
size="small"
<a-button
size="small"
:loading="isPlayingSynthesized"
:disabled="isPlayingSynthesized"
@click="playSynthesizedAudio"