语音合成
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user