🎙️ 如何在 HTML 页面中嵌入 TTS?JavaScript 调用 Flask API 实现网页语音合成
📝 引言:让网页“开口说话”——中文多情感语音合成的落地需求
随着智能客服、在线教育、无障碍阅读等场景的普及,文本转语音(Text-to-Speech, TTS)已成为提升用户体验的重要技术手段。尤其在中文语境下,用户对语音的自然度、情感表达和响应速度提出了更高要求。
传统的TTS服务多依赖第三方云平台,存在数据隐私、网络延迟和成本控制等问题。而基于开源模型如ModelScope 的 Sambert-Hifigan,我们可以在本地部署高质量的中文多情感语音合成服务,并通过Flask 提供 Web API,前端使用标准 HTML + JavaScript 调用,实现完全自主可控的语音生成功能。
本文将带你从零开始,构建一个完整的网页端 TTS 系统: - 后端:基于 ModelScope 的 Sambert-Hifigan 模型封装为 Flask 接口 - 前端:纯 HTML5 页面通过 AJAX 调用 API 实现语音合成与播放 - 集成:解决依赖冲突、优化推理性能、支持长文本合成
最终效果:用户在浏览器输入中文文本 → 点击按钮 → 后台生成.wav音频 → 前端自动播放或提供下载。
🔧 技术架构解析:Sambert-Hifigan + Flask + JavaScript 三位一体
✅ 核心组件分工明确
| 组件 | 角色 | 技术栈 | |------|------|--------| |Sambert-Hifigan 模型| 语音合成引擎 | ModelScope 开源模型,支持中文多情感 | |Flask 服务| 后端接口层 | Python + Flask + PyTorch | |HTML/JS 页面| 用户交互界面 | HTML5 + JavaScript (Fetch API) + Bootstrap |
该架构具备以下优势: -前后端分离:便于维护与扩展 -可复用性强:API 可被多个前端调用(Web、App、小程序) -离线可用:无需联网即可完成语音合成 -情感丰富:支持喜怒哀乐等多种情绪风格(具体情感类型取决于模型训练配置)
💡 关键突破点:
本项目已修复datasets(2.13.0)、numpy(1.23.5)与scipy(<1.13)的版本冲突问题,确保环境稳定运行,避免因依赖不兼容导致的崩溃。
🧩 后端实现:基于 ModelScope 的 Flask API 封装
我们将使用 ModelScope 提供的sambert-hifigan-tts-chinese模型,将其封装为一个 RESTful API 接口,接收文本并返回音频文件路径或二进制流。
1. 安装依赖(已优化版本兼容性)
pip install modelscope flask torch numpy==1.23.5 scipy==1.12.0 datasets==2.13.0⚠️ 特别注意:
scipy>=1.13会导致librosa加载失败,因此必须限制版本;numpy版本过高也会引发onnxruntime兼容问题。
2. 创建 Flask 应用:app.py
from flask import Flask, request, jsonify, send_file from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks import os import uuid app = Flask(__name__) OUTPUT_DIR = "outputs" os.makedirs(OUTPUT_DIR, exist_ok=True) # 初始化 TTS pipeline tts_pipeline = pipeline(task=Tasks.text_to_speech, model='damo/speech_sambert-hifigan_tts_zh-cn_16k') @app.route('/tts', methods=['POST']) def text_to_speech(): data = request.get_json() text = data.get('text', '').strip() if not text: return jsonify({'error': '请输入有效文本'}), 400 # 生成唯一文件名 filename = f"{uuid.uuid4().hex}.wav" output_path = os.path.join(OUTPUT_DIR, filename) try: # 执行语音合成 result = tts_pipeline(input=text, output_wav_path=output_path) return jsonify({ 'message': '语音合成成功', 'audio_url': f'/audio/{filename}' }) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/audio/<filename>') def serve_audio(filename): path = os.path.join(OUTPUT_DIR, filename) if os.path.exists(path): return send_file(path, mimetype='audio/wav') return '音频未找到', 404 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False)3. API 接口说明
| 接口 | 方法 | 参数 | 返回值 | |------|------|------|--------| |/tts| POST | JSON:{ "text": "你好,欢迎使用语音合成服务" }|{ "audio_url": "/audio/xxx.wav" }| |/audio/<filename>| GET | 文件名 | WAV 音频流 |
💻 前端开发:HTML + JavaScript 实现网页嵌入式 TTS
接下来我们编写一个简洁美观的 HTML 页面,允许用户输入文本并触发语音合成。
1. 完整 HTML 页面代码:index.html
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>中文多情感语音合成</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <style> body { padding: 40px; background-color: #f8f9fa; } .container { max-width: 700px; margin: 0 auto; } .form-control { border-radius: 8px; } .btn-primary { border-radius: 8px; } .alert { border-radius: 8px; } </style> </head> <body> <div class="container"> <h2 class="mb-4 text-center">🎙️ 中文多情感语音合成</h2> <p class="text-muted text-center mb-4">基于 ModelScope Sambert-Hifigan 模型,支持长文本合成</p> <div class="card shadow-sm"> <div class="card-body"> <div class="mb-3"> <label for="textInput" class="form-label">输入中文文本:</label> <textarea id="textInput" class="form-control" rows="4" placeholder="请输入要合成的中文内容..."></textarea> </div> <button id="synthesizeBtn" class="btn btn-primary w-100">开始合成语音</button> <div id="loading" class="text-center mt-3 d-none"> <div class="spinner-border text-primary" role="status"> <span class="visually-hidden">加载中...</span> </div> <small class="text-muted ms-2">语音生成中,请稍候...</small> </div> <div id="result" class="mt-4 d-none"> <h6>🎧 听一听:</h6> <audio id="audioPlayer" controls class="w-100"></audio> <a id="downloadLink" class="btn btn-outline-secondary mt-2 w-100" download>⬇️ 下载音频文件</a> </div> <div id="errorAlert" class="alert alert-danger mt-3 d-none"></div> </div> </div> </div> <script> const textInput = document.getElementById('textInput'); const synthesizeBtn = document.getElementById('synthesizeBtn'); const loading = document.getElementById('loading'); const result = document.getElementById('result'); const audioPlayer = document.getElementById('audioPlayer'); const downloadLink = document.getElementById('downloadLink'); const errorAlert = document.getElementById('errorAlert'); // 清除状态 function clearStatus() { errorAlert.classList.add('d-none'); result.classList.add('d-none'); loading.classList.add('d-none'); } // 发起语音合成请求 async function callTTS() { const text = textInput.value.trim(); if (!text) { showError('请输入有效的中文文本!'); return; } clearStatus(); loading.classList.remove('d-none'); try { const response = await fetch('http://localhost:5000/tts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text }) }); const data = await response.json(); if (response.ok && data.audio_url) { const audioUrl = data.audio_url; audioPlayer.src = audioUrl; downloadLink.href = audioUrl; result.classList.remove('d-none'); } else { showError(data.error || '语音合成失败'); } } catch (err) { showError('无法连接到后端服务,请检查 Flask 是否正在运行。'); } finally { loading.classList.add('d-none'); } } // 显示错误信息 function showError(msg) { errorAlert.textContent = msg; errorAlert.classList.remove('d-none'); } // 绑定事件 synthesizeBtn.addEventListener('click', callTTS); textInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); callTTS(); } }); </script> </body> </html>2. 功能亮点说明
- 响应式设计:基于 Bootstrap 实现移动端友好布局
- 一键合成:点击按钮或按回车(非 Shift+Enter)即可提交
- 实时反馈:显示加载动画与错误提示
- 播放+下载:支持
<audio>控件播放,同时提供.wav下载链接 - 跨域处理建议:若前后端分离部署,需在 Flask 中启用 CORS(可通过
flask-cors插件)
🛠️ 部署与调试:常见问题与解决方案
❌ 问题1:ModuleNotFoundError: No module named 'modelscope'
原因:未正确安装 ModelScope 包
解决:
pip install modelscope -f https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html❌ 问题2:OSError: [WinError 126] 找不到指定模块(Windows)
原因:缺少 Visual C++ 运行库或 ONNX Runtime 不兼容
解决:
pip uninstall onnxruntime pip install onnxruntime==1.15.1❌ 问题3:音频无法播放 / 返回 404
检查项: - 输出目录outputs/是否存在且有写权限 -output_wav_path是否正确传入 - 访问/audio/filename.wav路径是否匹配send_file路由
✅ 性能优化建议
- 缓存机制:对相同文本做 MD5 哈希,避免重复合成
- 异步队列:使用 Celery 或 threading 处理长文本合成,防止阻塞主线程
- CPU 优化:关闭不必要的日志输出,减少中间变量拷贝
- 压缩音频:可选返回 MP3 格式(需集成
pydub+ffmpeg)
🔄 使用流程回顾:三步实现网页语音合成
启动后端服务
bash python app.py服务监听
http://localhost:5000打开前端页面在浏览器中访问
index.html文件(推荐使用本地服务器,如python -m http.server 8000)输入文本并合成
- 输入任意中文文本
- 点击【开始合成语音】
- 等待几秒后自动播放音频,支持下载保存
🎯 总结:打造自主可控的中文语音合成系统
本文完整展示了如何将ModelScope 的 Sambert-Hifigan 多情感中文语音合成模型集成到网页应用中,通过Flask 提供 API 接口,前端使用原生 JavaScript 调用,实现了无需第三方依赖的本地化 TTS 解决方案。
✅ 核心价值总结
- 高保真语音:基于先进的端到端模型,语音自然流畅,支持多种情感表达
- 环境稳定:已修复关键依赖版本冲突,开箱即用
- 易于集成:标准 HTTP 接口,适用于 Web、App、IoT 等多种终端
- 完全自主:数据不出内网,保障隐私安全
🚀 下一步建议
- 增加情感选择器:在前端添加下拉菜单,选择“开心”、“悲伤”、“严肃”等情感模式
- 支持SSML标记语言:实现语速、停顿、重音等精细控制
- 容器化部署:使用 Docker 打包整个服务,便于迁移与发布
- 加入身份认证:为 API 添加 Token 验证,防止滥用
📌 最终目标:
构建一个企业级、可扩展、高可用的私有语音合成平台,替代昂贵的商业 API。
📚 附录:资源链接
- ModelScope 模型主页:https://modelscope.cn/models/damo/speech_sambert-hifigan_tts_zh-cn_16k
- Flask 官方文档:https://flask.palletsprojects.com/
- Bootstrap CDN:https://getbootstrap.com/
现在就动手试试吧!让你的网页真正“开口说话”。