在详细讲解之前,先看看最终的效果:
这个简单的应用只需要几分钟时间,你也可以搞定。
语音合成服务
语音合成服务有多种选择,比如阿里云、百度AI开放平台,AWS、七牛云等等,这里我选择的是七牛云,因为它价格便宜,而且API简单。
首先我们注册并登录七牛云,然后进入【个人中心>密钥管理】,生成一对密钥。
复制其中的AK(AccessKey)和SK(SecretKey),我们后续鉴权要使用。
接着我们可以自己写服务部署或者选择一个FaaS云服务。这里我选择AirCode,这是一个可以在线快速开发和部署云服务的平台。
然后我们创建一个AirCode项目:
在这个项目中,默认有一个hello.js,它是一个云函数,内容如下:
// @see https://docs.aircode.io/guide/functions/
const aircode = require('aircode');
module.exports = async function (params, context) {
console.log('Received params:', params);
return {
message: 'Hi, AirCode.',
};
};
我们把文件名修改成index.js
(或者不改也行,只是影响部署后的path),然后改写内容。
首先我们需要安装一些依赖,我们在IED右下角的Dependencies
下,安装node-fetch和qiniu这两个库。
然后我们配置七牛云的AK和SK。在IDE右上角的Environments中添加两个配置,将前面复制出来的七牛云密钥填入。
接下来,我们开始实现具体代码:
// @see https://docs.aircode.io/guide/functions/
const qiniu = require('qiniu');
const fetch = require('node-fetch');
const serviceURL = 'https://ap-gate-z0.qiniuapi.com/voice/v2/tts';
module.exports = async (params, context) => {
const {spkid, content} = params;
const mac = {accessKey: process.env.AK, secretKey: process.env.SK};
const body = JSON.stringify({spkid, content});
const authToken = qiniu.util.generateAccessTokenV2(mac, serviceURL, 'POST', 'application/json', body);
const res = await (await fetch(serviceURL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'authorization': authToken,
},
body,
})).json();
return res;
};
上面的代码非常简单,七牛云的语音合成API的服务地址是https://ap-gate-z0.qiniuapi.com/voice/v2/tts
,具体用法在它的文档中心可以查到:
developer.qiniu.com/dora/8091/s…
首先我们需要鉴权,鉴权方法在七牛云文档中:
developer.qiniu.com/kodo/3702/Q…
不过我们不必那么麻烦,我们前面已经安装了qiniu库,这是官方的SDK,因此我们只需要调用qiniu.util.generateAccessTokenV2
,就可以自动帮我们完成鉴权签名。
然后我们只需要带着签名请求头,请求serviceURL就可以获得我们想要的数据。
这时我们可以在IDE右侧调试区域进行调试,首先我们在Params中输入JSON数据:
{
"skpid":"7",
"content":"hi aircode"
}
然后点击右边的Debug
按钮,等待片刻,就能收到返回的数据,其中的audioUrl就是合成后的音频信息,你可以将URL复制到浏览器地址栏播放。
缓存音频数据
因为接口返回的数据是http协议,在很多场景中受到限制,所以我们可以用AirCode自带的文件存储服务将它转存。
另外,如果请求参数相同,我们可以不再重复生成音频,而是事先将音频数据缓存在数据库中,把缓存的数据返回给用户,因为七牛云毕竟是按照合成服务次数收费的,缓存音频数据能省下一些费用。
具体做法也非常简单,我们修改一下实现代码:
// @see https://docs.aircode.io/guide/functions/
const {parse} = require('node:url');
const path = require('node:path');
const {db, files} = require('aircode');
const qiniu = require('qiniu');
const fetch = require('node-fetch');
const serviceURL = 'https://ap-gate-z0.qiniuapi.com/voice/v2/tts';
module.exports = async (params, context) => {
const table = db.table('_files');
const {spkid, content} = params;
const body = JSON.stringify({spkid, content});
const cached = await table.where({body}).findOne();
if(cached) {
return {code: "0", result: {audioUrl: cached.url}, msg: "from cache"};
}
const mac = {accessKey: process.env.AK, secretKey: process.env.SK};
const authToken = qiniu.util.generateAccessTokenV2(mac, serviceURL, 'POST', 'application/json', body);
const res = await (await fetch(serviceURL, {
method: 'POST',
headers: {
'content-type': 'application/json',
'authorization': authToken,
},
body,
})).json();
if(res.code === "0") {
let url = res.result.audioUrl;
const filename = path.basename(parse(url).pathname);
const file = await files.upload({ url }, filename, {
additions: {
body,
}
});
url = file.url;
res.result.audioUrl = url;
}
return res;
};
在AirCode中,提供了默认的文件存储API,我们只需要调用files.upload方法,就可以把指定url的文件存储起来。AirCode会把文件信息存储在数据库的_files
表中。
我们在IDE下方的Database页下,可以看到_files
表。注意到,我们用additions: {body}
,将body信息,即请求的参数JSON字符串,也存储在这个数据表中。这样我们可以通过const cached = await table.where({body}).findOne();
来获取缓存的数据。
这样我们再运行Debug两次,就可以看到返回的数据是https协议的资源,而且是被缓存的。
部署应用
到这里,我们的服务端程序就写完了,我们可以部署应用。用AirCode部署应用特别简单,直接点击IDE上方的Deploy
按钮,等待部署完成后,直接访问文件下方的URL就可以。
前端界面
到目前为止我们已经实现了服务端代码,实现前端也很简单,首先是HTML:
<textarea id="content">Lodash 是一个流行的 JavaScript 实用工具库,提供了许多常用的函数和工具,能够方便地处理集合、字符串、数值、函数等多种数据类型,减少编写重复代码的时间和精力。Lodash 的 API 设计与 ES6 的新特性相似,同时兼容了更早的浏览器版本。Lodash 支持模块化加载,可以通过 npm 或在浏览器中直接引入来使用。
</textarea>
<div>
<button id="submitBtn">submit</button>
<select id="spkid">
<option value=7>女声:柔美</option>
<option value=8>女声:西安方言</option>
<option value=9>女声:东北方言</option>
<option value=10>男声:播音员</option>
<option value=11>男孩:活泼</option>
<option value=12>男声:配音</option>
<option value=13>男声:新闻联播</option>
<option value=14>女声:少女</option>
</select><span> <a target="blank" href="https://aircode.cool/pk7gjhdtat">参考代码</a></span>
<div id="notice"></div>
</div>
然后是CSS:
textarea {
width: 600px;
height: 300px;
}
最后是JS:
// See https://aircode.cool/pk7gjhdtat
const url = 'https://1263b3ejfu.us.aircode.run/index';
submitBtn.addEventListener('click', async (evt) => {
if(content.value) {
const data = {content: content.value, spkid: spkid.value};
notice.innerHTML = '音频合成中...';
const res = await (await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(data),
})).json();
console.log(res);
notice.innerHTML = `音频合成成功!<a href="${res.result.audioUrl}" target="_blank">${res.result.audioUrl}</a>`
}
});
你也可以直接Fork码上掘金的代码 code.juejin.cn/pen/7255992… 将const url = 'https://1263b3ejfu.us.aircode.run/index'
的地址改成你自己部署生成的应用的地址。
这样就实现了文字转语音的应用,七牛云的收费标准是合成千次3元钱,还是非常便宜的,你可以用它来生成各种语音,再将它合成到视频中或者作为分享音频,还是很有用的。
你学会了吗?可以自己动手尝试一下。关于这部分内容有任何问题,欢迎留言讨论。