赞
踩
demo模拟线上客户通话,实现tts播报和asr语音转文本,最终将tts和asr播放的文本进行存储或下载。
相关知识:
(1)网页媒体权限授权
(2)ArrayBuffer、TypeBuffer 截断、拼接
(3)音频流式文件播放方式
(4)ASR、TTS
工程相关:vue2
这个之前有写过
- // 方法就一个
- openMedia() {
- const that = this;
- const gotMediaStream = () => {
- that.getMedia = true;
- //这里是开始websocket的方法
- that.useWebSocket();
- };
- const handleError = (err) => {
- // console.log("navigator catch error", err);
- that.$message.error("未获录音权限");
- };
- if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
- // console.log("getusermedia is not supported!");
- that.$message.error("无录音权限");
- } else {
- const constrants = {
- audio: {
- volume: { min: 0.0, max: 1.0 },
- noiseSuppression: false,
- echoCancellation: false,
- },
- };
- navigator.mediaDevices
- .getUserMedia(constrants)
- .then(gotMediaStream)
- .catch(handleError);
- }
- },
- // 初始化的websocket
- useWebSocket() {
- const that = this;
- asrWs = new WebSocket(process.env.VUE_APP_WS_URL);
- asrWs.binaryType = "arraybuffer"; // 传输的是 ArrayBuffer 类型的数据
- asrWs.onopen = function () {
- if (asrWs.readyState === 1) {
- // 开启体验和心跳 但是未开始录音
- // that.experienceStatus = true;
- that.heartStart();
- }
- };
- asrWs.onmessage = function (msg) {
- //这里就是相关的业务逻辑了
- that.heartStart();
- };
- asrWs.onclose = function (err) {
- console.log(err);
- };
-
- asrWs.onerror = function (err) {
- that.heartStart();
- };
- },
修改了一些配置,这个配置解决录音音量较小的问题。
- navigator.mediaDevices
- .getUserMedia({audio:true })
- .then(gotMediaStream)
- .catch(handleError);
首先,这个 ArrayBuffer 类型化数组,类型化数组是JavaScript操作二进制数据的一个接口。最初为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式的背景下诞生的。
- var bf = new ArrayBuffer(40); // 生成了字节长度为40的内存区域
- //通过提供的 byteLength 属性返回分配字节的长度
- console.log(bf.byteLength); // 40
- /*
- 值得注意的是如果要分配的内存区域很大,有可能分配失败(因为没有那么多的连续空余内存),所以有必要检查是否分配成功。
- */
ArrayBuffer对象有一个slice方法,允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。
- const bf = new ArrayBuffer(40);
- const newBf = bf.slice(0, 10); // 从0 - 9 不包括 10
上面代码拷贝buffer对象的前10个字节,生成一个新的ArrayBuffer对象。slice方法其实包含两步,第一步是先分配一段新内存,第二步是将原来那个ArrayBuffer对象拷贝过去。
slice方法接受两个参数,第一个参数表示拷贝开始的字节序号,第二个参数表示拷贝截止的字节序号。如果省略第二个参数,则默认到原ArrayBuffer对象的结尾。
除了slice方法,ArrayBuffer对象不提供任何直接读写内存的方法,只允许在其上方建立视图,然后通过视图读写。
Int8Array:8位有符号整数,长度1个字节。
Uint8Array:8位无符号整数,长度1个字节。
Int16Array:16位有符号整数,长度2个字节。
Uint16Array:16位无符号整数,长度2个字节。
Int32Array:32位有符号整数,长度4个字节。
Uint32Array:32位无符号整数,长度4个字节。
Float32Array:32位浮点数,长度4个字节。
Float64Array:64位浮点数,长度8个字节。
下面这个文本有相关方法
JavaScript 之 ArrayBuffer_短暂又灿烂的的博客-CSDN博客
我的需求是将多个ArrayBuffer完成拼接
ASR:语音转文本,将语音转成对应的文本,然后做其他处理。
有http方式和websocket方式。
传送还是file,file属于blob 使用FormData
- export function updataBlob(data) {
- let formData = new FormData();
- formData.append("file", data);
- formData.append("chatId", store.getters.chatId);
- return request({
- url: "model/audio/upload",
- method: "post",
- data: formData,
- headers: {
- "Content-Type": "multipart/form-data",
- },
- });
- }
这种方式需要考虑是否为PCM和还是WAV格式的问题,后端解析需不需要加头
这种方式实时传送音频,对音频进行了切片。之前文章中也有代码。
https://mp.csdn.net/mp_blog/creation/editor/124618257
tts是将文本转成语音。
涉及语音就涉及播放。
(1)如果是一次性返回pcm格式文件,需要播放就需要加上头,wav格式。播放TTS返回的文件
添加依赖
"wav-headers": "^1.0.1"
var getFileHeaders = require("wav-headers");
添加wav请求头
- generateWav(buffer) {
- var options = {
- channels: 1,
- sampleRate: 16000,
- bitDepth: 16,
- dataLength: buffer.length,
- };
- var headersBuffer = getFileHeaders(options);
-
- var temp = new Uint8Array(buffer.byteLength + headersBuffer.byteLength);
- temp.set(new Uint8Array(headersBuffer), 0);
- temp.set(new Uint8Array(buffer), headersBuffer.byteLength);
- return temp;
- },
上传文件 new Blob([this.PCMList], { type: "audio/wav" }); 记住有个[ ] 本来this.PCMList 就是个TypeArray。
下面的生成wav文件下载。
- upAudioOne() {
- store.commit("audio/SET_AUDIO_CUT_SIZE", -1);
- this.PCMList = this.generateWav(this.PCMList);
- const blob = new Blob([this.PCMList], { type: "audio/wav" });
- updataBlob(blob);
-
- // let blobUrl = window.URL.createObjectURL(blob);
- // let link = document.createElement('a')
- // link.style.display = 'none'
- // link.href = blobUrl
- // link.download = 'test' + '.wav'
- // document.body.appendChild(link)
- // link.click()
-
- },
这个是生成wav文件 直接下载或播放
- if (Object.prototype.toString.call(data) == "[object Object]") {
- this.$message({
- message: data.message,
- type: "warning",
- duration: 3 * 1000,
- });
- } else {
- if (type) {
- this.cunrentAudioUrl = URL.createObjectURL(data);
- this.$refs.audio.volume = 0.1;
- } else {
- const reader = new FileReader();
- reader.readAsDataURL(data);
- reader.onload = (e) => {
- const a = document.createElement("a");
- a.download = item.id + ".wav";
- a.href = e.target.result;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- };
- }
- }
(2) 如果返回的是流式文件就需要将文件合成拼接在一起 记录TTS返回音频
- concatenate(resultConstructor, ...arrays) {
- let totalLength = 0;
- let startIndx = 0;
- if (!arrays[0].length) {
- this.PCMListAllIndex = [];
- } else {
- startIndx = arrays[0].length;
- }
- for (let arr of arrays) {
- totalLength += arr.length;
- }
- let result = new resultConstructor(totalLength);
- let offset = 0;
- for (let arr of arrays) {
- result.set(arr, offset);
- offset += arr.length;
- }
- this.PCMListAllIndex.push({ start: startIndx, end: totalLength });
- return result;
- },
使用方法合成
var dataAudio = new Uint8Array(msg.data);
if (that.ttsSart) {
that.PCMList = that.concatenate(
Uint8Array,
that.PCMList,
dataAudio
);
}
TTS流式播放
返回是ArrayBuffer ,这个写了看了一个第三方的做了修改
- var bufferSource = null;
-
- class PCMPlayer {
- constructor(option) {
- this.init(option);
- }
- // 初始化
- init(option) {
- // 默认参数
- const defaultOption = {
- inputCodec: "Int16", // 传入的数据是采用多少位编码,默认16位
- channels: 1, // 声道数
- sampleRate: 8000, // 采样率 单位Hz
- flushTime: 3000, // 缓存时间 单位 ms
- };
- this.option = Object.assign({}, defaultOption, option); // 实例最终配置参数
- this.samples = new Float32Array(); // 样本存放区域
- this.audioCtx = null;
- this.convertValue = this.getConvertValue();
- // 获取类型
- this.typedArray = this.getTypedArray();
- // 初始下音频上线文本
- this.initAudioContext();
- // 需要播放资源统计
- this.sourceCount = 0;
- // pcm二进制文件播放次数
- this.bufferSourceEndCount = 0;
- // 一次文本二进制文件全部返回会 与tts ws中的 speech_end 相关 设置成false
- this.bufferFeed = false;
- }
- // 设置 tts 传入状态, true: 传送中, false :未开始|传送结束
- setBufferFeedState(type = false) {
- this.bufferFeed = type;
- }
- // 创建浮点型数据数组
- // 初始某人值
- sourceLenInit() {
- this.samples = new Float32Array();
- // console.log("初始化=》被调用");
- this.sourceCount = 0;
- this.setBufferFeedState(true);
- this.bufferSourceEndCount = 0;
- }
- // 播放暂停 初始化参数值
- ttsClose() {
- this.sourceCount = 0;
- this.setBufferFeedState();
- this.bufferSourceEndCount = 0;
- if (bufferSource) {
- bufferSource.stop(0); //立即停止
- }
- this.destroy();
- }
- // 获取不同TypeArray类型的转化
- getConvertValue() {
- const inputCodecs = {
- Int8: 128,
- Int16: 32768,
- Int32: 2147483648,
- Float32: 1,
- };
- if (!inputCodecs[this.option.inputCodec])
- throw new Error(
- "wrong codec.please input one of these codecs:Int8,Int16,Int32,Float32"
- );
- return inputCodecs[this.option.inputCodec];
- }
- //获取TypeArray
- getTypedArray() {
- // 根据传入的目标编码位数
- // 选定前端的所需要的保存的二进制数据格式
- // 完整TypedArray请看文档
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
- const typedArrays = {
- Int8: Int8Array,
- Int16: Int16Array,
- Int32: Int32Array,
- Float32: Float32Array,
- };
- if (!typedArrays[this.option.inputCodec])
- throw new Error(
- "wrong codec.please input one of these codecs:Int8,Int16,Int32,Float32"
- );
- return typedArrays[this.option.inputCodec];
- }
- //初始化音频上下文
- initAudioContext() {
- this.sourceCount = 0;
- this.bufferSourceEndCount = 0;
- this.setBufferFeedState();
- // 初始化音频上下文的东西
- this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
- // 控制音量的 GainNode
- // https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/createGain
- //创建音量控制节点
- this.gainNode = this.audioCtx.createGain();
- this.gainNode.gain.value = 0.1;
- this.gainNode.connect(this.audioCtx.destination);
- this.startTime = this.audioCtx.currentTime;
- }
- // 检测 是否为TypeArray
- static isTypedArray(data) {
- // 检测输入的数据是否为 TypedArray 类型或 ArrayBuffer 类型
- return (
- (data.byteLength &&
- data.buffer &&
- data.buffer.constructor == ArrayBuffer) ||
- data.constructor == ArrayBuffer
- );
- }
- // 是否为 ArrayBuffer 或者 TypedArray
- isSupported(data) {
- // 数据类型是否支持
- // 目前支持 ArrayBuffer 或者 TypedArray
- if (!PCMPlayer.isTypedArray(data))
- throw new Error("请传入ArrayBuffer或者任意TypedArray");
- return true;
- }
- // pcm二进制文件输入(一句话多个二进制文件输入)
- feed(data) {
- if (!this.audioCtx) {
- return;
- }
- this.isSupported(data);
-
- // 获取格式化后的buffer
- data = this.getFormatedValue(data);
- // 开始拷贝buffer数据
- // 新建一个Float32Array的空间
- const tmp = new Float32Array(this.samples.length + data.length);
- // console.log(data, this.samples, this.samples.length)
- // 复制当前的实例的buffer值(历史buff)
- // 从头(0)开始复制
- tmp.set(this.samples, 0);
-
- // 复制传入的新数据
- // 从历史buff位置开始
- tmp.set(data, this.samples.length);
-
- // 将新的完整buff数据赋值给samples
- // interval定时器也会从samples里面播放数据
- this.samples = tmp;
-
- // 播放和回调的相关操作
- this.flush();
- }
- // 获取 对应类型数据的方法
- getFormatedValue(data) {
- if (data.constructor == ArrayBuffer) {
- data = new this.typedArray(data);
- } else {
- data = new this.typedArray(data.buffer);
- }
-
- let float32 = new Float32Array(data.length);
-
- for (let i = 0; i < data.length; i++) {
- // buffer 缓冲区的数据,需要是IEEE754 里32位的线性PCM,范围从-1到+1
- // 所以对数据进行除法
- // 除以对应的位数范围,得到-1到+1的数据
- // float32[i] = data[i] / 0x8000;
- float32[i] = data[i] / this.convertValue;
- }
- return float32;
- }
- // 音量控制
- volume(volume) {
- this.gainNode.gain.value = volume;
- }
- // 关闭上线文
- destroy() {
- this.samples = null;
- this.audioCtx.close();
- this.audioCtx = null;
- }
-
- // 每个音频文件
- flush() {
- const self = this;
-
- if (this.samples.length) {
- this.sourceCount += 1;
- }
-
- if (!this.samples.length) {
- return;
- }
- if (!this.audioCtx) {
- return;
- }
- bufferSource = this.audioCtx.createBufferSource();
-
- // 两个方法的回调
- if (
- typeof this.option.onended === "function" &&
- typeof this.option.allPlayed === "function"
- ) {
- bufferSource.onended = function (event) {
- self.bufferSourceEndCount += 1;
- self.option.allPlayed(
- self,
- self.bufferSourceEndCount == self.sourceCount && !self.bufferFeed
- );
- self.option.onended(this, event);
- };
- }
-
- const length = this.samples.length / this.option.channels;
- const audioBuffer = this.audioCtx.createBuffer(
- // 声道数
- this.option.channels,
- // 长度
- length,
- // 采样率
- this.option.sampleRate
- );
- // 多声道处理
- for (let channel = 0; channel < this.option.channels; channel++) {
- const audioData = audioBuffer.getChannelData(channel);
- let offset = channel;
- let decrement = 50;
- for (let i = 0; i < length; i++) {
- audioData[i] = this.samples[offset];
- /* fadein */
- if (i < 50) {
- audioData[i] = (audioData[i] * i) / 50;
- }
- /* fadeout*/
- if (i >= length - 51) {
- audioData[i] = (audioData[i] * decrement--) / 50;
- }
- offset += this.option.channels;
- }
- }
-
- if (this.startTime < this.audioCtx.currentTime) {
- this.startTime = this.audioCtx.currentTime;
- }
- // console.log(
- // "start vs current " +
- // this.startTime +
- // " vs " +
- // this.audioCtx.currentTime +
- // " duration: " +
- // audioBuffer.duration
- // );
- bufferSource.buffer = audioBuffer;
- bufferSource.connect(this.gainNode);
- bufferSource.start(this.startTime);
- // 添加开始播放的回调函数
- if (this.sourceCount == 1) {
- if (typeof this.option.firstPlay === "function") {
- self.option.firstPlay(this, Date.now());
- }
- }
- this.startTime += audioBuffer.duration;
- this.samples = new Float32Array();
- }
- // 暂停
- async pause() {
- await this.audioCtx.suspend();
- }
- // 继续播放
- async continue() {
- await this.audioCtx.resume();
- }
- // 绑定音频上线文的的状态 - 未使用
- bindAudioContextEvent() {
- const self = this;
- if (typeof self.option.onstatechange === "function") {
- this.audioCtx.onstatechange = function (event) {
- self.option.onstatechange(this, event, self.audioCtx.state);
- };
- }
- }
- // 获取音频上下文的状态
- getSate() {
- const self = this;
- return self.audioCtx.state;
- }
- }
-
- export default PCMPlayer;
使用页面
typeBuffer 加个wav 头用于播放 和上产后台
npm install wav-headers --save
- var ws = null; // 实现WebSocket
- import PCMPlayer from "./pcm-player.js";
- var getFileHeaders = require("wav-headers");
- export default {
- data() {
- return {
- timeoutTTs: 6000, // 6s发一次心跳,比server端设置的连接时间稍微小一点,在接近断开的情况下以通信的方式去重置连接时间。
- ttsTimeoutObj: null, // ws定时器
- ttsSart: false,
- ttsPlayStatus: false,
- player: null,
- PCMList: new Uint8Array(0),
- };
- },
- created() {
- // console.log('获取摄像头和麦克风权限')
- },
-
- mounted() {
- //初始化 PCMPlayer
- this.play();
- },
- beforeDestroy() {
- this.closeServe();
- this.ttsStop();
- },
-
- methods: {
- // 设置wav 头
- generateWav(buffer) {
- var options = {
- channels: 1,
- sampleRate: 16000,
- bitDepth: 16,
- dataLength: buffer.length,
- };
- var headersBuffer = getFileHeaders(options);
-
- var temp = new Uint8Array(buffer.byteLength + headersBuffer.byteLength);
- temp.set(new Uint8Array(headersBuffer), 0);
- temp.set(new Uint8Array(buffer), headersBuffer.byteLength);
- return temp;
- },
- // 拼接
- concatenate(resultConstructor, ...arrays) {
- let totalLength = 0;
- for (let arr of arrays) {
- totalLength += arr.length;
- }
- let result = new resultConstructor(totalLength);
- let offset = 0;
- for (let arr of arrays) {
- result.set(arr, offset);
- offset += arr.length;
- }
- return result;
- },
-
- // 初始化 PCMPlayer
- play() {
- const that = this;
- if (this.player) return;
- this.player = new PCMPlayer({
- encoding: "16bitInt",
- channels: 1,
- sampleRate: 16000,
- // 所有cpm 播放结束
- allPlayed: function (r, data) {
- console.log("allPlayed", data);
- },
- // 每个pcm 播放结束
- onended: function (r) {
-
- },
- // 音频上线状态
- onstatechange: function (r) {
-
- },
- });
- if (!ws) this.useTTSSocket();
- },
-
- // 播放暂停 用于打断使用
- ttsPlayEnd() {
- this.player.ttsClose();
- this.ttsPlayStatus = false;
- },
- // 摧毁状态
- ttsStop() {
- this.player.destroy();
- this.ttsPlayStatus = false;
- this.player = null;
-
- },
-
- // 关闭tts服务
- closeServe() {
- if (ws) {
- ws.close(); // 离开路由之后断开websocket连接
- ws = null;
- }
- // 2、通过关闭计时器和倒计时关闭心跳监测
- this.heartTTSReset();
- },
-
- // 心跳初重置
- heartTTSReset: function () {
- clearInterval(this.ttsTimeoutObj);
- },
-
- // 心跳初始化
- heartTTSStart() {
- const that = this;
- this.ttsTimeoutObj && clearInterval(this.ttsTimeoutObj);
- this.ttsTimeoutObj = setInterval(function () {
- if (ws.readyState === 1) {
- // console.log('连接状态,发送消息保持连接' + new Date())
- ws.send(JSON.stringify({ signal: "heart_beat" }));
- that.heartTTSStart();
- } else {
- that.useTTSSocket();
- }
- }, that.timeoutTTs);
- },
- // 初始化的websocket
- useTTSSocket() {
- const that = this;
- ws = new WebSocket("ws:/xx.xx.xx.xx:8000");
- // 传输的是 ArrayBuffer 类型的数据
- ws.binaryType = "arraybuffer";
- ws.onopen = function () {
- if (ws.readyState === 1) {
- that.heartTTSStart();
- }
- };
- ws.onmessage = function (msg) {
-
- if (!that.isArrayBuffer(msg.data)) {
- const backMsg = JSON.parse(msg.data);
- if (backMsg.message === "speech_start") {
- that.PCMList = [];
- that.player && that.player.sourceLenInit();
- that.ttsSart = true;
- }
- if (backMsg.message === "timestamp") {
- if (that.ttsSart) {
- that.setTxtProgress(backMsg.syllable_times);
- }
- // console.log("播放中");
- }
-
- if (backMsg.message === "speech_end") {
- that.ttsSart = false;
- that.player.bufferFeed = true;
- that.player && that.player.setBufferFeedState(false);
- }
- } else {
- const dataAudio = new Uint8Array(msg.data);
- if (that.ttsSart) {
- that.ttsPlayStatus = true;
- that.PCMList = that.concatenate(
- Uint8Array,
- that.PCMList,
- dataAudio
- );
- that.player.feed(dataAudio);
- }
- }
- that.heartTTSStart();
- };
- ws.onerror = function (err) {
- that.heartTTSStart();
- };
- },
- // 判断返回是否为 ArrayBuffer
- isArrayBuffer(data) {
- const strType = Object.prototype.toString.call(data);
- return strType.includes("ArrayBuffer");
- },
- // 开始发送文本
- startTTS(text) {
- const that = this;
- this.player.initAudioContext();
- setTimeout(() => {
- ws.send(
- JSON.stringify({
- text: text,
- speaker: "xiaoqian",
- pitch: 70,
- volume: 80,
- })
- );
- }, 0);
- },
- },
- };
audioBuffer转wav格式的方式
npm install audiobuffer-to-wav --save
截断获取
-
- watch: {
- //播放暂停状态
- ttsPlayStatus: {
- handler(val) {
- if (val) {
- this.playLongTime = Date.now();
- } else {
- //获取播放时间
- this.playLongTime = Date.now() - this.playLongTime;
- //这里很重要 decodeAudioData 需要的arrayBuffer是需要 有头的wav MP3都是可以
- //上一个 调用实现方法中 有PCMList 获取方式
- this.PCMList = this.generateWav(this.PCMList);
- // 调用截断的方法
- this.audioSilce(this.PCMList, this.playLongTime / 1000);
- }
- },
- },
- },
-
- //音频截断
- audioSilce(arrBuffer, time) {
- //兼容写法
- // window.AudioContext =
- // window.AudioContext ||
- // window.webkitAudioContext ||
- // window.mozAudioContext ||
- // window.msAudioContext;
- //创建音频上下文
- var audioCtx = new window.AudioContext();
-
- // var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
- //异步解析 ARRaybuffer
- audioCtx.decodeAudioData(arrBuffer.buffer, function (audioBuffer) {
- const channels = audioBuffer.numberOfChannels;
- // 声道数量和采样率
- // var channels = audioBuffer.numberOfChannels;
- const rate = audioBuffer.sampleRate;
-
- // 获取截断的音频
- const startOffset = 0;
- const endOffset = rate * time;
- // 对应的帧数
- const frameCount = endOffset - startOffset;
-
- // 创建同样采用率、同样声道数量,长度是前3秒的空的AudioBuffer
- const newAudioBuffer = new AudioContext().createBuffer(
- channels,
- endOffset - startOffset,
- rate
- );
- // 创建临时的Array存放复制的buffer数据
- const anotherArray = new Float32Array(frameCount);
- // 声道的数据的复制和写入
- const offset = 0;
- for (let channel = 0; channel < channels; channel++) {
- audioBuffer.copyFromChannel(anotherArray, channel, startOffset);
- newAudioBuffer.copyToChannel(anotherArray, channel, offset);
- }
-
- //播放的方法 方便验证
- // setTimeout(() => {
- // var source = audioCtx.createBufferSource();
- // source.buffer = newAudioBuffer;
- // source.connect(gainNode);
- // source.start(0);
- // source.onended = function (event) {
- // console.log("播放结束");
- // };
- // }, 1000);
-
- const wav = toWav(newAudioBuffer);
- const blob = new Blob([wav], { type: "audio/wav" });
- const url = URL.createObjectURL(blob);
- const a = document.createElement("a");
- a.download = "测试.wav";
- a.href = url;
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- });
- },
引入js
import PCMPlayer from "./pcm-player.js";
方法中调用
this.player.sourcePlayLen 和 playLen 是所有 流式的ArrayBuffer播放完成的判断。
原理:
使用音频上下文及createBufferSource() 实时播放二进制流
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
bufferSource = this.audioCtx.createBufferSource(); 见pcm-player.js源码
每个ArrayBuffer都会播放,每次播放播放会调用 onended方法,记录 每次feed方法的调用的次数(就是sourcePlayLen)和个文件播放完成 onended 次数做个判断。相等就播放完成。
firstPlay这个回调是 sourcePlayLen= 1的的回调,判断音频播放中状态的回调方法。这些和业务相关,自己可以补充回调方法。
- var dataAudio = new Uint8Array(msg.data);
- if (that.ttsSart) {
- that.PCMList = that.concatenate(
- Uint8Array,
- that.PCMList,
- dataAudio
- );
-
- that.txtProgreeArr.push(that.sorcePushConut);
- // that.ttsPlayStatus = true;
- that.player && that.player.feed(dataAudio);
- that.sorcePushConut += 1;
- }
- play() {
- const that = this;
- if (this.player) return;
- this.player = new PCMPlayer({
- encoding: "16bitInt",
- channels: 1,
- sampleRate: 16000,
- flushTime: 1000,
- firstPlay: function (r) {
- that.ttsPlayStatus = true;
- },
- onended: function (r) {
- that.playLen += 1;
- if (that.playLen === that.player.sourcePlayLen) {
- that.playLen = 0;
- that.player.sourceLenInit();
- // 正常结束
- that.ttsIsInterrupt = true;
- that.ttsPlayStatus = false;
- that.ttsOnend();
- if (that.chatSate == 4) {
- that.ttsStop();
- }
- }
- },
- })
- },
主要的几个方法
创建:this.player = new PCMPlayer({})
暂停:this.player.ttsClose();
摧毁:this.player.destroy();
以上是在项目中,是使用到TTS、ASR不同协议对音频的上传、下载、播放、等功能,用得到请点赞谢谢。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。