专栏名称: 奇舞精选
《奇舞精选》是由奇舞团维护的前端技术公众号。除周五外,每天向大家推荐一篇前端相关技术文章,每周五向大家推送汇总周刊内容。
目录
相关文章推荐
笔记侠  ·  小红书,为何成了新的热点讨论场? ·  15 小时前  
江西宣传  ·  DeepSeek首次公开辟谣! ·  21 小时前  
电商技术每天分享  ·  淘宝详情页服务器双图技术 ·  2 天前  
电商技术每天分享  ·  淘宝详情页服务器双图技术 ·  2 天前  
娱乐资本论  ·  不走2024弯路,今年春节档胜在“合家欢”+ ... ·  2 天前  
娱乐资本论  ·  不走2024弯路,今年春节档胜在“合家欢”+ ... ·  2 天前  
51好读  ›  专栏  ›  奇舞精选

利用 Speech-AI-Forge 优化语音编辑器的实现

奇舞精选  · 公众号  · 科技自媒体  · 2024-12-16 18:32

主要观点总结

文章介绍了如何通过引入开源项目Speech-AI-Forge来优化TTS生成,增强语音编辑器的功能和用户体验。包括Speech-AI-Forge简介、安装与运行、语音编辑器的功能优化、前端功能优化及MultiAudioPlayer插件代码等。

关键观点总结

关键观点1: 引入Speech-AI-Forge实现TTS生成的全面优化

Speech-AI-Forge是一个开源的TTS生成工具,支持自定义语音角色、语气风格、以及基于SSML的文本格式化。通过其强大的API接口,可以替代传统的Web Speech API,生成更高质量的音频资源。

关键观点2: SSML支持扩展与TTS接口对接

扩展编辑器的SSML生成逻辑,使其与Speech-AI-Forge完美对接。通过调用Speech-AI-Forge的/v1/audio/speech接口,生成音频文件。

关键观点3: 背景音与段落同步播放功能

利用自定义的MultiAudioPlayer插件,实现背景音与段落内容的同步播放。通过传入生成的内容音频contentUrl和背景音轨bgInfo.url,用户可以实时试听内容。

关键观点4: 前端功能优化

包括动态音频管理、段落编辑改进、语音角色与语气选择、MultiAudioPlayer插件代码等。通过这些优化,提高了语音编辑器的效率和用户体验。


正文

image.png

在上一篇中,我们基于浏览器原生的 SpeechSynthesis API 构建了一个基础语音编辑器。本篇将通过引入开源项目 Speech-AI-Forge,实现对 TTS 生成的全面优化,增强语音编辑器的功能和用户体验。


Speech-AI-Forge 简介

Speech-AI-Forge 是一个开源的 TTS 生成工具,支持自定义语音角色、语气风格、以及基于 SSML 的文本格式化。通过其强大的 API 接口,我们可以轻松替代传统的 Web Speech API,生成更高质量的音频资源。

安装与运行

brew install ffmpeg
brew install rubberband
pip install -r requirements.txt
python launch.py

运行后可通过 http://localhost:7870/docs 查看 API 文档。

mac运行会报cpu错误,建议使用 Docker 部署

docker-compose -f ./docker-compose.api.yml up -d

部署后,通过以下命令测试生成的音频:

curl http://localhost:7870/v1/audio/speech \
-H "Content-Type: application/json" \
-d '{
"model": "chattts",
"input": "Today is a wonderful day to build something people love! [lbreak]",
"voice": "female2",
"style": "chat"
}' \
--output speech.mp3

语音编辑器的功能优化

1. SSML 支持扩展

Speech-AI-Forge 提供的 SSML 支持包括:

  • voice :定义角色及其语气风格。
  • prosody :控制语速、音高和音量。
  • break :插入暂停。

我们可以扩展编辑器的 SSML 生成逻辑,使其与 Speech-AI-Forge 完美对接。

优化后的 SSML 生成逻辑

getssml(data) { const ssml = []; const regex = /( ] >. ?)/g; const parts = data.content.split(regex).filter(part => part.trim());

parts.forEach(item => { if (item.startsWith('

  if (dataType === 'speed') {
ssml.push(``);
} else if (dataType === 'break') {
ssml.push(``);
}
} else {
ssml.push(item);
}

});

const roleId = data.roleId || ''; const toneId = data.toneId || ''; return ${ssml.join('')} ; }

2. TTS 接口对接

通过调用 Speech-AI-Forge 的 /v1/audio/speech 接口,生成音频文件。

音频生成函数

async function generateAudio(ssml, model = 'chattts', voice = 'female2', style = 'chat') { const response = await fetch('http://localhost:7870/v1/audio/speech', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model, input: ssml, voice, style }), });

const audioBlob = await response.blob(); const audioUrl = URL.createObjectURL(audioBlob); return audioUrl; }

3. 背景音与段落同步播放

利用自定义的 MultiAudioPlayer 插件,实现背景音与段落内容的同步播放。

改进后的播放逻辑

this.$multiAudioPlayer.play([contentUrl, bgInfo.url]); 通过传入生成的内容音频 contentUrl 和背景音轨 bgInfo.url ,用户可以实时试听内容。


前端功能优化

1. 动态音频管理

将背景音选择与内容音频整合,通过新增一个工具方法管理生成与播放:

音频预览工具

async previewAudio(paragraph) { const ssml = this.getssml(paragraph); const contentUrl = await generateAudio(ssml); const backgroundUrl = paragraph.backgroundUrl || '';

this.$multiAudioPlayer.play([contentUrl, backgroundUrl]); }

2. 段落编辑改进

在插入 break speed 时,精确调整插入位置:

// 停顿可按照用户鼠标位置插入 addBreak(breakNum) { const range = this.rangeInfo.range; const styledElement = this.commonAddStyle( 停顿${breakNum}s , 'break', breakNum * 1000); range.deleteContents(); range.insertNode(styledElement); } // 倍速只能插在一段文本中最后位置 addSpeed(speed) { let refID = editor_${this.rangeInfo.domInfo.id} let curDom = this.$refs[refID][0] const spans = curDom.getElementsByTagName('span') let found = false for (const span of spans) { // 检查 data-type 属性是否为 'speed',存在的话更改显示值和num if (span.getAttribute('data-type') === 'speed') { found = true span.setAttribute('data-num', speed) const textSpan = span.querySelector('.text-content') textSpan.textContent = `倍速${speed} break } } if (found) return const styledElement = this.commonAddStyle( 倍速${speed} , 'speed', speed) curDom.appendChild(styledElement) }, 通过 range` 获取光标位置,插入停顿标签,并确保文档结构不被破坏。

3. 语音角色与语气选择

通过调用 Speech-AI-Forge 提供的角色与语气接口,动态加载用户可选的选项。

接口.png
音色.png
语气.png

4. MultiAudioPlayer 插件代码

class MultiAudioPlayer { constructor() { this.audios = []; // 存储多个 Audio 对象 this.currentTime = 0; // 当前播放时间 this.duration = 0; // 总时长(取最短音频) this.isPlaying = false; // 播放状态 this.updateProgress = null; // 播放进度的回调函数 this.minDurationAudio = null; // 保存最短音频的引用 }

// 加载并播放多个音频 play(urls) { this.audios = urls.map(url => { const audio = new Audio(url); // 创建 Audio 实例 return audio; });

// 确定最短的音频总时长
this.audios.forEach(audio => {
audio.addEventListener('loadedmetadata', () => {
if (!this.duration || audio.duration < this.duration) {
this.duration = audio.duration;
this.minDurationAudio = audio; // 设置最短的音频
}
});
});

// 播放所有音频
this.audios.forEach(audio => {
audio.play();
});
this.isPlaying = true;

// 更新播放进度并同步
this.audios.forEach(audio => {
audio.addEventListener('timeupdate', () => {
this.currentTime = audio.currentTime;
if (this.updateProgress) {
this.updateProgress(this.currentTime, this.duration);
}
});
});

// 当最短的音频结束时,停止所有音频
this.minDurationAudio.addEventListener('ended', () => {
this.stopAll();
});

}

// 停止所有音频的播放 stopAll() { this.audios.forEach(audio => { audio.pause(); audio.currentTime = 0; }); this.isPlaying = false; this.currentTime = 0; }

// 暂停所有音频 pause() { this.audios.forEach(audio => { audio.pause(); }); this.isPlaying = false; }







请到「今天看啥」查看全文