<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button onclick="recStart()">开始</button>
<button onclick="recStop()">停止</button>
<script src="./recorder-core.js"></script>
<script src="./pcm.js"></script>
<script>
var testSampleRate=16000;
var testBitRate=16;
var SendFrameSize=3200;/**** 每次发送指定二进制数据长度的数据帧,单位字节,16位pcm取值必须为2的整数倍,8位随意。
16位16khz的pcm 1秒有:16000hz*16位/8比特=32000字节的数据,默认配置3200字节每秒发送大约10次
******/
//重置环境,每次开始录音时必须先调用此方法,清理环境
var RealTimeSendTryReset=function(){
realTimeSendTryChunks=null;
};
var realTimeSendTryNumber;
var transferUploadNumberMax;
var realTimeSendTryChunk;
var realTimeSendTryChunks;
//调用录音
var rec;
function recStart() {
if (rec) {
rec.close();
};
rec = Recorder({
type: "unknown",
onProcess: function (buffers, powerLevel, bufferDuration, bufferSampleRate) {
//推入实时处理,因为是unknown格式,buffers和rec.buffers是完全相同的,只需清理buffers就能释放内存。
RealTimeSendTry(buffers, bufferSampleRate, false);
}
});
var t = setTimeout(function () {
console.log("无法录音:权限请求被忽略(超时假装手动点击了确认对话框)", 1);
}, 8000);
rec.open(function () {//打开麦克风授权获得相关资源
clearTimeout(t);
rec.start();//开始录音
RealTimeSendTryReset();//重置环境,开始录音时必须调用一次
}, function (msg, isUserNotAllow) {
clearTimeout(t);
console.log((isUserNotAllow ? "UserNotAllow," : "") + "无法录音:" + msg, 1);
});
};
// 停止录音
function recStop(){
rec.close();//直接close掉即可,这个例子不需要获得最终的音频文件
RealTimeSendTry([],0,true);//最后一次发送
};
//=====实时处理核心函数==========
var RealTimeSendTry = function (buffers, bufferSampleRate, isClose) {
if (realTimeSendTryChunks == null) {
realTimeSendTryNumber = 0;
transferUploadNumberMax = 0;
realTimeSendTryChunk = null;
realTimeSendTryChunks = [];
};
//配置有效性检查
if (testBitRate == 16 && SendFrameSize % 2 == 1) {
console.log("16位pcm SendFrameSize 必须为2的整数倍", 1);
return;
};
var pcm = [], pcmSampleRate = 0;
if (buffers.length > 0) {
//借用SampleData函数进行数据的连续处理,采样率转换是顺带的,得到新的pcm数据
var chunk = Recorder.SampleData(buffers, bufferSampleRate, testSampleRate, realTimeSendTryChunk);
//清理已处理完的缓冲数据,释放内存以支持长时间录音,最后完成录音时不能调用stop,因为数据已经被清掉了
for (var i = realTimeSendTryChunk ? realTimeSendTryChunk.index : 0; i < chunk.index; i++) {
buffers[i] = null;
};
realTimeSendTryChunk = chunk;//此时的chunk.data就是原始的音频16位pcm数据(小端LE),直接保存即为16位pcm文件、加个wav头即为wav文件、丢给mp3编码器转一下码即为mp3文件
pcm = chunk.data;
pcmSampleRate = chunk.sampleRate;
if (pcmSampleRate != testSampleRate)//除非是onProcess给的bufferSampleRate低于testSampleRate
throw new Error("不应该出现pcm采样率" + pcmSampleRate + "和需要的采样率" + testSampleRate + "不一致");
};
//将pcm数据丢进缓冲,凑够一帧发送,缓冲内的数据可能有多帧,循环切分发送
if (pcm.length > 0) {
realTimeSendTryChunks.push({ pcm: pcm, pcmSampleRate: pcmSampleRate });
};
//从缓冲中切出一帧数据
var chunkSize = SendFrameSize / (testBitRate / 8);//8位时需要的采样数和帧大小一致,16位时采样数为帧大小的一半
var pcm = new Int16Array(chunkSize), pcmSampleRate = 0;
var pcmOK = false, pcmLen = 0;
for1: for (var i1 = 0; i1 < realTimeSendTryChunks.length; i1++) {
var chunk = realTimeSendTryChunks[i1];
pcmSampleRate = chunk.pcmSampleRate;
for (var i2 = chunk.offset || 0; i2 < chunk.pcm.length; i2++) {
pcm[pcmLen] = chunk.pcm[i2];
pcmLen++;
//满一帧了,清除已消费掉的缓冲
if (pcmLen == chunkSize) {
pcmOK = true;
chunk.offset = i2 + 1;
for (var i3 = 0; i3 < i1; i3++) {
realTimeSendTryChunks.splice(0, 1);
};
break for1;
}
}
};
//缓冲的数据不够一帧时,不发送 或者 是结束了
if (!pcmOK) {
if (isClose) {
var number = ++realTimeSendTryNumber;
TransferUpload(number, null, 0, null, isClose);
};
return;
};
//16位pcm格式可以不经过mock转码,直接发送new Blob([pcm.buffer],{type:"audio/pcm"}) 但8位的就必须转码,通用起见,均转码处理,pcm转码速度极快
var number = ++realTimeSendTryNumber;
var encStartTime = Date.now();
var recMock = Recorder({
type: "pcm"
, sampleRate: testSampleRate //需要转换成的采样率
, bitRate: testBitRate //需要转换成的比特率
});
recMock.mock(pcm, pcmSampleRate);
recMock.stop(function (blob, duration) {
blob.encTime = Date.now() - encStartTime;
//转码好就推入传输
TransferUpload(number, blob, duration, recMock, false);
//循环调用,继续切分缓冲中的数据帧,直到不够一帧
RealTimeSendTry([], 0, isClose);
}, function (msg) {
//转码错误?没想到什么时候会产生错误!
console.log("不应该出现的错误:" + msg, 1);
});
};
var createInputStream = function(buffer) {
let position = 0;
return {
read() {
if (position >= buffer.length) {
return -1; // EOF
}
const byte = buffer[position];
position += 1;
return byte;
},
reset() {
position = 0;
},
close() {
// 可选:释放资源或其他清理操作
},
};
}
//=====数据传输函数==========
var TransferUpload=function(number,blobOrNull,duration,blobRec,isClose){
transferUploadNumberMax=Math.max(transferUploadNumberMax,number);
if(blobOrNull){
var blob=blobOrNull;
var encTime=blob.encTime;
//*********发送方式一:Base64文本发送***************
var reader=new FileReader();
reader.onloadend=function(){
var base64=(/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result)||[])[1];
var arrayBuffer = reader.result;
const inputStream = createInputStream(new Uint8Array(arrayBuffer));
const byte = inputStream.read();
console.log(byte);
//可以实现
//WebSocket send(base64) ...
//WebRTC send(base64) ...
//XMLHttpRequest send(base64) ...
//这里啥也不干
};
// reader.readAsDataURL(blob);
reader.readAsArrayBuffer(blob);
//*********发送方式二:Blob二进制发送***************
//可以实现
//WebSocket send(blob) ...
//WebRTC send
OCR语音转文字OCR语音转文字
需积分: 0 2 浏览量
2024-05-15
09:41:04
上传
评论
收藏 31KB ZIP 举报
![avatar](https://profile-avatar.csdnimg.cn/default.jpg!1)
qq_43765727
- 粉丝: 10
- 资源: 2
最新资源
- 数据库管理工具:dbeaver-ce-23.2.4-stable.x86-64.rpm
- 含小数的十进制转N进制源代码.rar
- 数据库管理工具:dbeaver-ce-23.2.4-macos-x86-64.dmg
- python的字符界面程序
- 数据库管理工具:dbeaver-ce-23.2.3-stable.x86-64.rpm
- 数据库管理工具:dbeaver-ce-23.2.3-macos-x86-64.dmg
- 数据库管理工具:dbeaver-ce-23.2.3-macos-aarch64.dmg
- 乐播投屏 5.9.02版.apk
- 数据库管理工具:dbeaver-ce-23.2.1-x86-64-setup.exe
- 高分项目,基于Unity3D开发实现的贪吃蛇游戏,内含完整源码+资源+视频教程
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![feedback](https://img-home.csdnimg.cn/images/20220527035711.png)
![feedback](https://img-home.csdnimg.cn/images/20220527035711.png)
![feedback-tip](https://img-home.csdnimg.cn/images/20220527035111.png)