nodejs版讯飞文字合成语音
本帖最后由 alwaysol 于 2021-6-30 14:51 编辑最近想听一部小说但是没语音版,看到讯飞有文字转语音的接口所有就玩玩,官方原版例子一次只能生成3000个字左右,所以我修改了下把整个小说分片段生成语音,如有不足的地方望大佬们指正
步骤:
1.去nodejs官网下载win版的nodejs.exe,直接安装就行,不需要任何设置
2.去讯飞开放平台注册账号,开通语音合成业务,可以免费用50000次,复制APPID等信息到以下代码config对应的位置
3.把下载的小说txt文件放到当前目录下,txt要utf-8编码的,如果不是另存txt为utf-8编码
4.修改代码里的txt文件名和你想保存的音频文件名
5.双击start.bat文件就会出现dos窗口,音频文件生成在当前目录下MP3文件夹里
源代码下载地址:https://wwa.lanzouj.com/iPLAJniey6f
/* Created by iflytek on 2020/03/01.
*
* 运行前:请先填写 appid、apiSecret、apiKey
*
* 在线语音合成调用demo
* 此demo只是一个简单的调用示例,不适合用到实际生产环境中
*
* 在线语音合成 WebAPI 接口调用示例 接口文档(必看):https://www.xfyun.cn/doc/tts/online_tts/API.html
* 错误码链接:
* https://www.xfyun.cn/document/error-code (code返回错误码时必看)
*
*/
const CryptoJS = require('crypto-js');
const WebSocket = require('ws');
const log = require('log4node');
const fs = require('fs');
//在控制台-我的应用-在线语音合成(流式版)获取
let appid = "";
//在控制台-我的应用-在线语音合成(流式版)获取
let apiSecret = "";
//在控制台-我的应用-在线语音合成(流式版)获取
let apiKey = "";
let vcn = "xiaoyan";//声音
var txt = fs.readFileSync('新建文本文档.txt', 'utf8');//读取的文件名
var flie = "newtxt";//保存的文件名
var min = 10;//每个音频时长,分钟
var array = (txt.length % 3000) > 0 ? parseInt(txt.length / 3000 + 1) : parseInt(txt.length / 3000);
min = min % 10 > 0 ? parseInt(min / 10) + 1 : parseInt(min / 10);
(async () => {
var page = (array % 100) > 0 ? parseInt(array / 100 + 1) : parseInt(array / 100);
for (let i = 0; i < page; i++) {
for (let index = i * 100; index < (i * 100) + 100; index++) {
main(index);
}
await wait(1000 * 20);
}
})();
function main(index) {
// 系统配置
var config = {
// 请求地址
hostUrl: "wss://tts-api.xfyun.cn/v2/tts",
host: "tts-api.xfyun.cn",
appid: appid,
apiSecret: apiSecret,
apiKey: apiKey,
text: txt.substr(3000 * index, 3000),
uri: "/v2/tts",
}
// 获取当前时间 RFC1123格式
let date = (new Date().toUTCString());
// 设置当前临时状态为初始化
let wssUrl = config.hostUrl + "?authorization=" + getAuthStr(date) + "&date=" + date + "&host=" + config.host;
let ws = new WebSocket(wssUrl);
if (!fs.existsSync('./mp3/' + flie)) fs.mkdirSync('./mp3/' + flie);
// 连接建立完毕,读取数据进行识别
ws.on('open', () => {
log.info("websocket connect!");
send();
// 如果之前保存过音频文件,删除之
var ss = (index + 1);
ss = ss % min > 0 ? parseInt(ss / min) + 1 : ss / min;
if (fs.existsSync('./mp3/' + flie + '/' + flie + ss + '.mp3')) {
fs.unlink('./mp3/' + flie + '/' + flie + ss + '.mp3', (err) => {
if (err) {
log.error('remove error: ' + err);
}
})
}
})
// 得到结果后进行处理,仅供参考,具体业务具体对待
ws.on('message', async (data, err) => {
if (err) {
log.error('message error: ' + err);
return;
}
let res = JSON.parse(data);
if (res.code != 0) {
log.error(`${res.code}: ${res.message}`);
ws.close();
return;
}
let audio = res.data.audio;
let audioBuf = Buffer.from(audio, 'base64');
await save(audioBuf);
if (res.code == 0 && res.data.status == 2) {
await wait(1000);
var ss = (index + 1);
ss = ss % min > 0 ? parseInt(ss / min) + 1 : ss / min;
log.info('第' + ss + '个文件合并完成,共' + (array % min > 0 ? parseInt(array / min) + 1 : parseInt(array / min)) + '个文件*********************!');
ws.close();
}
})
// 资源释放
ws.on('close', () => {
log.info('connect close!');
});
// 连接错误
ws.on('error', (err) => {
log.error("websocket connect err: " + err);
})
// 鉴权签名
function getAuthStr(date) {
let signatureOrigin = `host: ${config.host}\ndate: ${date}\nGET ${config.uri} HTTP/1.1`;
let signatureSha = CryptoJS.HmacSHA256(signatureOrigin, config.apiSecret);
let signature = CryptoJS.enc.Base64.stringify(signatureSha);
let authorizationOrigin = `api_key="${config.apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`;
let authStr = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(authorizationOrigin));
return authStr;
}
// 传输数据
function send() {
let frame = {
// 填充common
"common": {
"app_id": config.appid
},
// 填充business
"business": {
"aue": "lame",
"sfl": 1,
"auf": "audio/L16;rate=16000",
"vcn": vcn,
"tte": "UTF8"
},
// 填充data
"data": {
"text": Buffer.from(config.text).toString('base64'),
"status": 2
}
}
ws.send(JSON.stringify(frame));
}
// 保存文件
async function save(data) {
return new Promise(async resolve => {
var ss = (index + 1);
ss = ss % min > 0 ? parseInt(ss / min) + 1 : ss / min;
fs.writeFile('./mp3/' + flie + '/' + flie + ss + '.mp3', data, { flag: 'a' }, (err) => {
if (err) {
log.error('save error: ' + err);
resolve();
return;
}
log.info('正下载第' + ss + '个文件碎片!');
resolve();
})
})
}
}
async function wait(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
})
}
感谢楼主分享,软件非常好用,使用后,发现一个小问题,因为合成的音频,是由很多小片段合并而来,合并处(小片段的拼接处)会出现破音或者撕裂的声音,一部小说转下来,很多都会有这个问题,不知道有没有优化的可能? fj198602 发表于 2021-6-9 23:33
感谢楼主分享,软件非常好用,使用后,发现一个小问题,因为合成的音频,是由很多小片段合并而来,合并处( ...
好的,有时间我看看 大神,什么时候有时间,能优化一下。查了好多资料,看到一个答案:说是JS数据格式的问题。 本人小白,不太懂,不知道能不能给大神一点帮助。
页:
[1]