1,掉后端接口选择模式。
2,分为实时和非实时语音识别。(没有后端配合看不到效果展示)
3,点击开始,ws建联,sendData对获取的数据处理,分包处理每包1024.tmparr式处理过的音频。
4,点击关闭,链接断开。
5,再次点击开始,刷新文本框。
6,上传音频文件识别。(file音频文件做处理模拟蹦字效果)
7,再次使用录音功能时,先清除file上传的内容。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="apple-mobile-web-capable" content="yes">
<title>ASR demo</title>
<style type="text/css">
html body {
margin: 0;
padding: 0;
}
.manage {
display: flex;
justify-content: space-between;
}
.comments {
width: 100%; /*自动适应父布局宽度*/
overflow: auto;
word-break: break-all;
/*在ie中解决断行问题(防止自动变为在一行显示,主要解决ie兼容问题,ie8中当设宽度为100%时,文本域类容超过一行时,
当我们双击文本内容就会自动变为一行显示,所以只能用ie的专有断行属性“word-break或word-wrap”控制其断行)*/
}
img{
display: none;
}
</style>
</head>
<body>
<div class="manage">
<div id="controls">
<button id="intercomBegin">开始</button>
<button id="intercomEnd">关闭</button>
</div><br>
<!-- <input type="button" onclick="" value="下载"> -->
<a href="音频地址" downlown=""></a>
<!-- 上传音频 accept打开系统文件目录 -->
<div class="file">
<form id="file-rec" method="POST" action="#" target="myIframe" enctype="multipart/form-data">
<input type="file" id="fileInput" accept=".pcm,.wav" multiple="multiple"/>
<div id="audio1"></div> <!-- 音频播放器 -->
<button id="upload">上传</button>
</form>
<iframe style="display: none" id="myIframe" name="myIframe" class="iframes"></iframe>
</div>
</div>
<div>选择模式</div>
<form id="article-foot" class="article-foot">
</form><br/>
</body>
<!-- 引入组件 -->
<script src="./js/jquery.min.js"></script>
<script src="./js/toastr.min.js"></script> <!-- 基于jquery的非阻塞的消息提示插件 -->
<link rel="stylesheet" href="./css/toastr.min.css">
<script src="./js/HZRecorder.js"></script>
<!-- <script src="./js/audio.js"></script> -->
<script type="text/javascript">
var result = ''
var textResult = document.getElementById("textResult");
var begin = document.getElementById('intercomBegin');
var end = document.getElementById('intercomEnd');
var ws = null; //实现WebSocket
var record = null; //多媒体对象,用来处理音频
var interval; //定时器
function init(rec) { //init初始化
record = rec;
console.log(record)
}
//生成唯一标识符
function uuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
uuid() // "ffb7cefd-02cb-4853-8238-c0292cf988d5"
var uuid = uuid()
//console.log(uuid)
/*ajax请求数据选择模式 */
window.onload=function() {
$.ajax({
type: "GET",
async: false,
url: "http://10.45.38.250:8998/asr/non_real/selectModel" ,
dataType: "json",
success: function (data) {
//console.log(data)
if (data.length == 0) {
//如果查出来的数据为空,则div隐藏
$(".article-foot").hide();
} else {
$(".article-foot").show();
// console.log(data)
var html = ''
$.each(data,function(i,n){
html += "<input type=\"checkbox\" name=\"items\" value=\""+n.type+"\">" + n.name + "</input>" + "<br>"
+"<textarea id=\"textResult\" class=\"comments\" rows=\"10\" cols=\"10\"></textarea>"
// console.log(html)
});
$(".article-foot").html(html);
}
}
});
}
/* message提示 */
function showMessage(message,type,time) {
let str = ''
switch (type) {
case 'success':
str = '<div class="success-message" style="width: 300px;height: 40px;text-align: center;background-color:#daf5eb;;color: rgba(59,128,58,0.7);position: fixed;left: 43%;top: 10%;line-height: 40px;border-radius: 5px;z-index: 9999">\n' +
' <span class="mes-text">'+message+'</span></div>'
break;
case 'error':
str = '<div class="error-message" style="width: 300px;height: 40px;text-align: center;background-color: #f5f0e5;color: rgba(238,99,99,0.8);position: fixed;left: 43%;top: 10%;line-height: 40px;border-radius: 5px;;z-index: 9999">\n' +
' <span class="mes-text">'+message+'</span></div>'
}
$('body').append(str)
setTimeout(function () {
$('.'+type+'-message').remove()
},time)
}
/*
* 开始对讲
*/
begin.onclick = function() {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
//点击开始清空文本框
// clearFile()
result=''
var comments = document.getElementsByClassName('comments')
console.log(comments)
for (var i = 0; i < comments.length; i++) {
comments[i].innerHTML = ''
}
if (!navigator.getUserMedia) {
alert('浏览器不支持音频输入');
} else {
navigator.getUserMedia({
audio: true
},
//判断
function(mediaStream) {
init(new Recorder(mediaStream));
console.log(mediaStream)
console.log('开始对讲');
showMessage('建立连接中','success',1000)
useWebSocket();
//如果ws链接还在,就断开ws
if(ws == true) {
ws.close()
}
},
function(error) {
console.log(error);
switch (error.message || error.name) {
case 'PERMISSION_DENIED':
case 'PermissionDeniedError':
console.info('用户拒绝提供信息。');
break;
case 'NOT_SUPPORTED_ERROR':
case 'NotSupportedError':
console.info('浏览器不支持硬件设备。');
break;
case 'MANDATORY_UNSATISFIED_ERROR':
case 'MandatoryUnsatisfiedError':
console.info('无法发现指定的硬件设备。');
break;
default:
console.info('无法打开麦克风。异常信息:' + (error.code || error.name));
break;
}
}
)
}
}
/*
* 关闭对讲
*/
end.onclick = function() {
if (ws) {
ws.send(JSON.stringify({'msgType': 'audioEnd'}))
console.log('关闭对讲以及WebSocket');
//ws.send(JSON.stringify({'msgType': 'end'}))
//ws.close();
record.stop();
}
clearInterval(interval)
}
/* file文件上传 */
//上传音频
$("#fileInput").change(function(){
var file = $("#fileInput").get(0).files;
console.log()
console.log($("#fileInput").get(0))
console.log(file);
for(var i = 0;i<file.length;i++){
var path = URL.createObjectURL(file[i]); //用于创建 URL 的 File 对象.
//console.log(path)
//console.log(file[i])
}
});
$("#upload").click(function(){
// console.log((new Date().getTime()))
result = ''
var comments = document.getElementsByClassName('comments')
console.log(comments)
for (var i = 0; i < comments.length; i++) {
comments[i].innerHTML = ''
}
//ajax上传至后台
// uploadAudio(formData,arr);
//调用websocket
useWebSocket();
});
/* 清除file上传的文件内容 */
function clearFile(){
$('#fileInput').val('')
var file = $("#fileInput").get(0).files
//console.log(file)
}
//Recorder
var Recorder = function(stream) {
var sampleBits = 16; //输出采样数位 8, 16
var sampleRate = 16000; //输出采样率(单位时间内对模拟信号采样的多少)
var context = new AudioContext();
var audioInput = context.createMediaStreamSource(stream);
var recorder = context.createScriptProcessor(4096, 1, 1);
//录音模型
var audioData = {
size: 0, //录音文件长度
buffer: [], //录音缓存
inputSampleRate: 16000, //输入采样率
inputSampleBits: 16, //输入采样数位 8, 16
outputSampleRate: sampleRate, //输出采样数位
oututSampleBits: sampleBits, //输出采样率
clear: function() {
this.buffer = [];
this.size = 0;
},
input: function(data) {
this.buffer.push(new Float32Array(data));
this.size += data.length;
},
compress: function() { //合并压缩
//合并
var data = new Float32Array(this.size);
var offset = 0;
for (var i = 0; i < this.buffer.length; i++) {
data.set(this.buffer[i], offset);
offset += this.buffer[i].length;
}
//压缩
var compression = parseInt(this.inputSampleRate / this.outputSampleRate);
//console.log(compression)
var length = data.length / compression;
var result = new Float32Array(length);
var index = 0,
j = 0;
while (index < length) {
result[index] = data[j];
j += compression;
index++;
}
return result;
},
encodePCM: function() {
var sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate);
var sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits);
var bytes = this.compress();
var dataLength = bytes.length * (sampleBits / 8);
var buffer = new ArrayBuffer(dataLength);
var data = new DataView(buffer);
var offset = 0;
for (var i = 0; i < bytes.length; i++, offset += 2) {
var s = Math.max(-1, Math.min(1, bytes[i]));
//数据视图指定字节偏移量位置处设置 Int16 值
data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
return new Blob([data]);
}
};
var sendData = function() { //对以获取的数据进行处理(分包),每包最大1024
var reader = new FileReader(); // FileReader接口提供了读取文件的方法和包含读取 结果的事件模型
reader.onload = e => {
var outbuffer = e.target.result; //e是监听到时间回调返回的event对象,event.target 是事件属性,可返回事件的目标节点
var arr = new Int8Array(outbuffer);
if (arr.length > 0) {
var tmparr = new Int8Array(1024); //Int8Array:8位有符号整数
var j = 0;
for (var i = 0; i < arr.byteLength; i++) {
tmparr[j++] = arr[i];
if (((i + 1) % 1024) == 0) {
ws.send(tmparr); //处理过的音频数据
if (arr.byteLength - i - 1 >= 1024) {
tmparr = new Int8Array(1024);
} else {
tmparr = new Int8Array(arr.byteLength - i - 1);
}
j = 0;
}
if ((i + 1 == arr.byteLength) && ((i + 1) % 1024) != 0) {
ws.send(tmparr);
}
}
}
};
reader.readAsArrayBuffer(audioData.encodePCM());
audioData.clear();//每次发送完成则清理掉旧数据
};
this.start = function() {
audioInput.connect(recorder);
recorder.connect(context.destination);
}
this.stop = function() {
recorder.disconnect();
}
this.getBlob = function() {
return audioData.encodePCM();
}
this.clear = function() {
audioData.clear();
}
recorder.onaudioprocess = function(e) {
var inputBuffer = e.inputBuffer.getChannelData(0);
audioData.input(inputBuffer);
sendData();
console.log('发送音频流')
}
}
/* */
/*
* WebSocket
*/
function useWebSocket() {
ws = new WebSocket("ws://10.45.38.250:8998/asr/audio/recognition");
// console.log(ws)
// ws.binaryType = 'arraybuffer'; //传输的是 ArrayBuffer 类型的数据
ws.onopen = function() { //连接成功建立的回调方法
// console.log((new Date().getTime()))
ws.binaryType='text'
var s = $("input[name='items']")
var arr= []
s.each(function(i) {
if(this.checked == true) {
arr.push(this.value)
}
})
ws.send(JSON.stringify({'asrType' : arr, 'msgType' : 'init' }))
console.log('发送初始化');
//sleep(5000)
//console.log(1111111)
if (ws.readyState == 1) { //ws进入连接状态,则每隔40毫秒发送一包数据
var file = $("#fileInput").get(0).files; //判断音频文件的
// console.log(file[0]) //undefined+
if("undefined" == typeof file[0]) {
interval =setInterval(() => {
record.start()
},40)
}else {
var fr = new FileReader();
var filename = file[0].name;
var buf
fr.readAsArrayBuffer(file[0]);
fr.addEventListener("loadend",(e) => {
buf = e.target.result;
//console.log(buf.byteLength)
//var temp =0;
for (var i = 0; i < buf.byteLength; i += 3840) {
var arr = buf.slice(i,i+3840);
// console.log(arr)
//temp = i;
ws.send(arr)
//console.log(arr.byteLength)
}
//sleep(buf.byteLength / 1280 * 40 )
ws.send(JSON.stringify({'msgType': 'audioEnd'}))
console.log('发送sendEnd')
//清除file文件
clearFile()
},false);
}
}
};
//接收到消息的回调方法
ws.onmessage = function(MesssageEvent) {
//返回结果
//console.log((new Date().getTime()))
//console.log(MesssageEvent)
var jsonStr = MesssageEvent.data;
//console.log(jsonStr)
var jso = JSON.parse(jsonStr)
//console.log(jso)
var inputs = document.getElementsByName("items")
for (var i = 0; i < inputs.length; i ++) { //遍历数组
if(inputs[i].value == jso.type){
if(jso.type == 'real'){
if(jso.action == 'final') {
//console.log(result)
result =( result + jso.result) + "\r\n" //回车和换行
}
if(jso.action == 'partial') {
inputs[i].nextElementSibling.nextElementSibling.innerHTML = result + jso.result //获取下个兄弟元素
}
}
if(jso.type == 'non_real'){
if(jso.action == 'final'){
inputs[i].nextElementSibling.nextElementSibling.innerHTML += jso.result + "\r\n"
}
}
}
}
}
//连接关闭的回调方法
ws.onerror = function(err) {
console.info(err)
textResult.innerHTML = ''
}
//关闭websocket连接
ws.onclose = function (msg) {
console.info(msg);
};
//文本框根据输入内容自适应高度
var autoTextarea = function (elem, extra, maxHeight) {
//判断elem是否为数组
if (elem.length > 0) {
for (var i = 0; i < elem.length; i++) {
e(elem[i]);
}
} else {
e(elem);
}
function e(elem) {
extra = extra || 0;
var isFirefox = !!document.getBoxObjectFor || 'mozInnerScreenX' in window,
isOpera = !!window.opera && !!window.opera.toString().indexOf('Opera'),
addEvent = function (type, callback) {
elem.addEventListener ?
elem.addEventListener(type, callback, false) :
elem.attachEvent('on' + type, callback);
},
getStyle = elem.currentStyle ? function (name) {
var val = elem.currentStyle[name];
if (name === 'height' && val.search(/px/i) !== 1) {
var rect = elem.getBoundingClientRect();
return rect.bottom - rect.top -
parseFloat(getStyle('paddingTop')) -
parseFloat(getStyle('paddingBottom')) + 'px';
}
;
return val;
} : function (name) {
return getComputedStyle(elem, null)[name];
},
minHeight = parseFloat(getStyle('height'));
elem.style.resize = 'none';
var change = function () {
var scrollTop, height,
padding = 0,
style = elem.style;
if (elem._length === elem.value.length) return;
elem._length = elem.value.length;
if (!isFirefox && !isOpera) {
padding = parseInt(getStyle('paddingTop')) + parseInt(getStyle('paddingBottom'));
}
;
scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
elem.style.height = minHeight + 'px';
if (elem.scrollHeight > minHeight) {
if (maxHeight && elem.scrollHeight > maxHeight) {
height = maxHeight - padding;
style.overflowY = 'auto';
} else {
height = elem.scrollHeight - padding;
style.overflowY = 'hidden';
}
;
style.height = height + extra + 'px';
scrollTop += parseInt(style.height) - elem.currHeight;
document.body.scrollTop = scrollTop;
document.documentElement.scrollTop = scrollTop;
elem.currHeight = parseInt(style.height);
}
;
};
addEvent('propertychange', change);
addEvent('input', change);
addEvent('focus', change);
change();
}
};
}
</script>
</html>