Estoy participando en la convocatoria de ensayos para tecnología cruzada. Para obtener más información, consulte: juejin.cn/post/710123...
prefacio
He probado varias soluciones TTS y, después de un poco de experiencia, descubrí que Microsoft es el rey en este campo y Azure文本转语音
que el efecto de voz convertido de sus servicios es el más natural, pero Azure es un servicio pago y es demasiado problemático para pagar. para operaciones de registro. Pero en su sitio web oficial, en realidad proporciona una 完全体
función de demostración, que puede experimentar completamente las voces y los estilos de habla de todos los personajes...
Pero simplemente no se puede descargar como un mp3
archivo, por lo que algunos amigos tienen que transcribir la voz de la computadora para obtener el archivo de audio, pero esto es demasiado problemático. De hecho, todos los recursos que se pueden ver y escuchar en la página web son el resultado del descifrado. Es decir, siempre que el sonido se reproduzca desde la página web, debemos encontrar la manera de extraer el archivo de audio.
Este artículo es para registrar todo el proceso de exploración y realización, disfrute ~
La mayor parte del contenido de este artículo fue escrito a principios de este año y aún no se ha publicado. Sé que una vez que este método se haga público, pronto será bloqueado por Microsoft, o incluso cancelará directamente la entrada y las interfaces relacionadas de la experiencia web.
Analizar la función de demostración del sitio web oficial de Azure
Use el navegador Chrome para abrir el panel de depuración Cuando hacemos clic en la 播放
función en el sitio web oficial de Azure, podemos monitorear una wss://
solicitud desde la pestaña de red, que es una websocket
solicitud.
dos parámetros
En la solicitud URL
, podemos ver que hay dos parámetros Authorization
yX-ConnectionId
Curiosamente, el primer parámetro está en el código fuente de la página web y puede extraerlo directamente solicitando axios
esta URL de texto a voz de Azure.get
const res = await axios.get("https://azure.microsoft.com/en-gb/services/cognitive-services/text-to-speech/");
const reg = /token: \"(.*?)\"/;
if(reg.test(res.data)){
const token = RegExp.$1;
}
复制代码
Al ver la pila de llamadas JS que inició la solicitud, haga clic en reproducir nuevamente después de agregar un punto de interrupción
Se puede encontrar que el segundo parámetro X-ConnectionId
proviene de una createNoDashGuid
función
this.privConnectionId = void 0 !== t ? t : s.createNoDashGuid(),
复制代码
这就是一个uuid v4
格式的字符串,nodash
就是没有-
的意思。
三次发送
请求时URL里的两个参数已经搞定了,我们继续分析这个webscoket
请求,从Message标签中可以看到
每次点击播放时,都向服务器上报了三次数据,明显可以看出来三次上报数据各自的作用
第一次的数据:SDK版本,系统信息,UserAgent
Path: speech.config
X-RequestId: 818A1E398D8D4303956D180A3761864B
X-Timestamp: 2022-05-27T16:45:02.799Z
Content-Type: application/json
{"context":{"system":{"name":"SpeechSDK","version":"1.19.0","build":"JavaScript","lang":"JavaScript"},"os":{"platform":"Browser/MacIntel","name":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36","version":"5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36"}}}
复制代码
第二次的数据:转语音输出配置,从outputFormat
可以看出来,最终的音频格式为audio-24khz-160kbitrate-mono-mp3
,这不就是我们想要的mp3
文件吗?!
Path: synthesis.context
X-RequestId: 091963E8C7F342D0A8E79125EA6BB707
X-Timestamp: 2022-05-27T16:48:43.340Z
Content-Type: application/json
{"synthesis":{"audio":{"metadataOptions":{"bookmarkEnabled":false,"sentenceBoundaryEnabled":false,"visemeEnabled":false,"wordBoundaryEnabled":false},"outputFormat":"audio-24khz-160kbitrate-mono-mp3"},"language":{"autoDetection":false}}}
复制代码
第三次的数据:要转语音的文本信息和角色voice name
,语速rate
,语调pitch
,情感等配置
Path: ssml
X-RequestId: 091963E8C7F342D0A8E79125EA6BB707
X-Timestamp: 2022-05-27T16:48:49.594Z
Content-Type: application/ssml+xml
<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US"><voice name="zh-CN-XiaoxiaoNeural"><prosody rate="0%" pitch="0%">我叫大帅,一个热爱编程的老程序猿</prosody></voice></speak>
复制代码
接收的二进制消息
既然从前三次上报的信息已经看出来返回的格式就是mp3
文件了,那么我们是不是把所有返回的二进制数据合并就可以拼接成完整的mp3
文件了呢?答案是肯定的!
每次点击播放后接收的所有来自websocket
的消息的最后一条,都有明确的结束标识符
turn.end
代表转换结束!
用Node.js实现它
既然都解析出来了,剩下的就是在Node.js
中重新实现这个过程。
两个参数
- Authorization,直接通过axios的get请求抓取网页内容后通过正则表达式提取
const res = await axios.get("https://azure.microsoft.com/en-gb/services/cognitive-services/text-to-speech/");
const reg = /token: \"(.*?)\"/;
if(reg.test(res.data)){
const Authorization = RegExp.$1;
}
复制代码
- X-ConnectionId,直接使用
uuid
库即可
//npm install uuid
const { v4: uuidv4 } = require('uuid');
const XConnectionId = uuidv4().toUpperCase();
复制代码
创建WebSocket连接
//npm install nodejs-websocket
const ws = require("nodejs-websocket");
const url = `wss://eastus.tts.speech.microsoft.com/cognitiveservices/websocket/v1?Authorization=${Authorization}&X-ConnectionId=${XConnectionId}`;
const connect = ws.connect(url);
复制代码
三次发送
第一次发送
function getXTime(){
return new Date().toISOString();
}
const message_1 = `Path: speech.config\r\nX-RequestId: ${XConnectionId}\r\nX-Timestamp: ${getXTime()}\r\nContent-Type: application/json\r\n\r\n{"context":{"system":{"name":"SpeechSDK","version":"1.19.0","build":"JavaScript","lang":"JavaScript","os":{"platform":"Browser/Linux x86_64","name":"Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0","version":"5.0 (X11)"}}}}`;
connect.send(message_1);
复制代码
第二次发送
const message_2 = `Path: synthesis.context\r\nX-RequestId: ${XConnectionId}\r\nX-Timestamp: ${getXTime()}\r\nContent-Type: application/json\r\n\r\n{"synthesis":{"audio":{"metadataOptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":false},"outputFormat":"audio-16khz-32kbitrate-mono-mp3"}}}`;
connect.send(message_2);
复制代码
第三次发送
const SSML = `
<speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" xmlns:emo="http://www.w3.org/2009/10/emotionml" version="1.0" xml:lang="en-US">
<voice name="zh-CN-XiaoxiaoNeural">
<mstts:express-as style="general">
<prosody rate="0%" pitch="0%">
我叫大帅,一个热爱编程的老程序猿
</prosody>
</mstts:express-as>
</voice>
</speak>
`
const message_3 = `Path: ssml\r\nX-RequestId: ${XConnectionId}\r\nX-Timestamp: ${getXTime()}\r\nContent-Type: application/ssml+xml\r\n\r\n${SSML}`
connect.send(message_3);
复制代码
接收二进制消息拼接mp3
当三次发送结束后我们通过connect.on('binary')
监听websocket
接收的二进制消息。
Cree un objeto Buffer vacío final_data
y luego empalme el contenido binario recibido cada vez en final_data
él. Una vez que el mensaje de texto normal contenga un Path:turn.end
identificador, se final_data
escribirá y creará en un mp3
archivo.
let final_data=Buffer.alloc(0);
connect.on("text", (data) => {
if(data.indexOf("Path:turn.end")>=0){
fs.writeFileSync("test.mp3",final_data);
connect.close();
}
})
connect.on("binary", function (response) {
let data = Buffer.alloc(0);
response.on("readable", function () {
const newData = response.read()
if (newData)data = Buffer.concat([data, newData], data.length+newData.length);
})
response.on("end", function () {
const index = data.toString().indexOf("Path:audio")+12;
final_data = Buffer.concat([final_data,data.slice(index)]);
})
});
复制代码
De esta manera, guardamos con éxito el mp3
archivo de audio sin siquiera abrir el sitio web oficial de Azure.
herramienta de línea de comandos
He empaquetado todo el código en una herramienta de línea de comandos, es muy fácil de usar
npm install -g mstts-js
mstts -i 文本转语音 -o ./test.mp3
复制代码
Todo código abierto: github.com/ezshine/mst…
usar en uni-aplicación
Crear una nueva función en la nube
Cree una nueva función en la nube y asígnele un nombremstts
Como mstss-js
ha sido encapsulado, solo necesita estar en la función de la nube y npm install mstts-js
luego require
el código es el siguiente
'use strict';
const mstts = require('mstts-js')
exports.main = async (event, context) => {
const res = await mstts.getTTSData('要转换的文本','CN-Yunxi');
//res为buffer格式
});
复制代码
Descargar y reproducir archivos mp3
Para reproducir este archivo en formato mp3 en uniapp, hay dos formas
Método 1. Cargue primero en el almacenamiento en la nube y acceda a través de la dirección de almacenamiento en la nube
exports.main = async (event, context) => {
const res = await mstts.getTTSData('要转换的文本','CN-Yunxi');
//res为buffer格式
var uploadRes = await uniCloud.uploadFile({
cloudPath: "xxxxx.mp3",
fileContent: res
})
return uploadRes.fileID;
});
复制代码
Uso frontal:
uniCloud.callFunction({
name:"mstts",
success:(res)=>{
const aud = uni.createInnerAudioContext();
aud.autoplay = true;
aud.src = res;
aud.play();
}
})
复制代码
- Pros: seguridad de la función de la nube
- Desventaja: Subir archivos al almacenamiento en la nube sin un mecanismo de limpieza desperdiciará espacio
Método 2. Utilice la URLización + respuesta integrada de las funciones de la nube para acceder
Este método consiste en convertir directamente el cuerpo de respuesta de la función de la nube en un archivo mp3, al que audio.src
se puede acceder directamente a través de la asignación .
exports.main = async (event, context) => {
const res = await mstts.getTTSData('要转换的文本','CN-Yunxi');
return {
mpserverlessComposedResponse: true,
isBase64Encoded: true,
statusCode: 200,
headers: {
'Content-Type': 'audio/mp3',
'Content-Disposition':'attachment;filename=\"temp.mp3\"'
},
body: res.toString('base64')
}
};
复制代码
Uso frontal:
const aud = uni.createInnerAudioContext();
aud.autoplay = true;
aud.src = 'https://ezshine-274162.service.tcloudbase.com/mstts';
aud.play();
复制代码
- Pros: fácil de usar, no es necesario guardar archivos en el almacenamiento en la nube
- Desventaja: si la función de nube URLizada no tiene un mecanismo de seguridad, otros pueden usarla arbitrariamente después de ser capturada.
resumen
Una biblioteca tan útil tts
, si te ayuda, no olvides github
apoyarla star
.
Soy 大帅
, un programa que ama 老
programar 猿
. WeChat personal:dashuailaoyuan