天峻县公司网站建设怎么做网站的内链外链
- 作者: 五速梦信息网
- 时间: 2026年03月21日 08:19
当前位置: 首页 > news >正文
天峻县公司网站建设,怎么做网站的内链外链,网站和h5,中山企业网站建设1、主题描述
兼容多个浏览器下的前端录音功能#xff0c;实现六大录音功能#xff1a;
1、开始录音
2、暂停录音
3、继续录音
4、结束录音
5、播放录音
6、上传录音
2、示例功能
初始状态#xff1a; 开始录音#xff1a; 结束录音#xff1a; 录音流程 #xf…1、主题描述
兼容多个浏览器下的前端录音功能实现六大录音功能
1、开始录音
2、暂停录音
3、继续录音
4、结束录音
5、播放录音
6、上传录音
2、示例功能
初始状态 开始录音 结束录音 录音流程
示例中的三个按钮其实包含了六个上述功能点击开始时开始录音可以暂停/结束录音此操作后就可以播放播音/上传录音了噢~以下是对应六大录音功能示例代码那大家会发现HZRecorder是啥呢 其实 HZRecorder 是录音类我们调用的都是该类里面的方法。
那大家肯定好奇录音是通过怎样一种形式存在呢其实用的就是浏览器的AudioContext对象他旨在创建一个音频dom有输入和输出。具体想了解这对象的可以去mdn看看
AudioContext /*** 录音前准备 检查录音设备是否到位/this.readyRecording async function() {let recorder // 表示录音类实例// 流模式下ready钩子 res 为录音类实例 或者 falseawait HZRecorder.ready().then(res {recorder res})return recorder}/** 开始录音/this.startRecording function() {recorder.start();}/** 结束录音/this.stopRecording function() {recorder.end();}/** 播放录音/this.playRecording function() {recorder.play(audio);}/** 继续录音/this.resumeRecord function() {recorder.again();}/** 暂停录音/this.pauseRecord function() {recorder.stop();}/** 重新录音/this.reRecord function() {this.startRecording()}/** 上传录音/this.uploadRecord function() {// 流模式下上传recorder.upload(url, succ, fail)}
3、流模式下的录音类
大家看到这标题就好奇啥叫流模式下的录音类呢那还有其他模式吗的确我总结了下是根据上传录音时的数据来区分的~我们常规情况下上传录音都是流模式也就是Content-Type为application/octem-stream源码如下
/** 录音类针对content-type为application/octem-stream 的使用* param {*} stream * param {*} config */
const HZRecorder function (stream, config) {config config || {};config.sampleBits config.sampleBits || 8; //采样数位 8, 16 config.sampleRate config.sampleRate || (44100 / 6); //采样率(1⁄6 44100) //创建一个音频环境对象 audioContext window.AudioContext || window.webkitAudioContext;var context new audioContext();//将声音输入这个对像 var audioInput context.createMediaStreamSource(stream);//设置音量节点 var volume context.createGain();audioInput.connect(volume);//创建缓存用来缓存声音 var bufferSize 4096;// 创建声音的缓存节点createScriptProcessor方法的 // 第二个和第三个参数指的是输入和输出都是双声道。 var recorder context.createScriptProcessor(bufferSize, 2, 2);var audioData {size: 0 //录音文件长度 , buffer: [] //录音缓存 , inputSampleRate: context.sampleRate //输入采样率 , inputSampleBits: 16 //输入采样数位 8, 16 , outputSampleRate: config.sampleRate //输出采样率 , oututSampleBits: config.sampleBits //输出采样数位 8, 16 , 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);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;}, encodeWAV: 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(44 dataLength);var data new DataView(buffer);var channelCount 1;//单声道 var offset 0;var writeString function (str) {for (var i 0; i str.length; i) {data.setUint8(offset i, str.charCodeAt(i));}};// 资源交换文件标识符 writeString(RIFF); offset 4;// 下个地址开始到文件尾总字节数,即文件大小-8 data.setUint32(offset, 36 dataLength, true); offset 4;// WAV文件标志 writeString(WAVE); offset 4;// 波形格式标志 writeString(fmt ); offset 4;// 过滤字节,一般为 0x10 16 data.setUint32(offset, 16, true); offset 4;// 格式类别 (PCM形式采样数据) data.setUint16(offset, 1, true); offset 2;// 通道数 data.setUint16(offset, channelCount, true); offset 2;// 采样率,每秒样本数,表示每个通道的播放速度 data.setUint32(offset, sampleRate, true); offset 4;// 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8 data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset 4;// 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8 data.setUint16(offset, channelCount * (sampleBits / 8), true); offset 2;// 每样本数据位数 data.setUint16(offset, sampleBits, true); offset 2;// 数据标识符 writeString(data); offset 4;// 采样数据总数,即数据总大小-44 data.setUint32(offset, dataLength, true); offset 4;// 写入采样数据 if (sampleBits 8) {for (var i 0; i bytes.length; i, offset) {var s Math.max(-1, Math.min(1, bytes[i]));var val s 0 ? s * 0x8000 : s * 0x7FFF;val parseInt(255 / (65535 / (val 32768)));data.setInt8(offset, val, true);}} else {for (var i 0; i bytes.length; i, offset 2) {var s Math.max(-1, Math.min(1, bytes[i]));data.setInt16(offset, s 0 ? s * 0x8000 : s * 0x7FFF, true);}}return new Blob([data], { type: audio/wav });}};//开始录音 this.start function () {audioInput.connect(recorder);recorder.connect(context.destination);};//停止 this.stop function () {recorder.disconnect();};// 结束this.end function () {context.close();};// 继续this.again function () {recorder.connect(context.destination);};//获取音频文件 this.getBlob function () {this.stop();return audioData.encodeWAV();};//回放 this.play function (audio) {audio.src window.URL.createObjectURL(this.getBlob());};//上传 this.upload function (url, succ, fail) {const xhr new XMLHttpRequest();xhr.overrideMimeType(application/octet-stream)// xhr.upload.addEventListener(progress, function (e) {// }, false);xhr.addEventListener(load, function (e) {succ(xhr.response)}, false);xhr.addEventListener(error, function (e) {fail(xhr.response);}, false);xhr.addEventListener(abort, function (e) {fail(xhr.response);}, false);xhr.open(POST, url);if(xhr.sendAsBinary){xhr.sendAsBinary(this.getBlob());}else{xhr.send(this.getBlob());}};//音频采集 recorder.onaudioprocess function (e) {audioData.input(e.inputBuffer.getChannelData(0));};
}/*** 多浏览器兼容* param {} videoConfig 参数配置 param {} succ 成功回调 param {} fail 失败回调 returns promise/
HZRecorder.compatibleMedia async function(videoConfig) {let streamPromise // 视频promiseif (navigator.mediaDevices navigator.mediaDevices.getUserMedia){// 最新标准APIstreamPromise await navigator.mediaDevices.getUserMedia(videoConfig)} else if (navigator.webkitGetUserMedia){// webkit内核浏览器streamPromise await navigator.webkitGetUserMedia(videoConfig)} else if (navigator.mozGetUserMedia){// Firefox浏览器streamPromise await navagator.mozGetUserMedia(videoConfig)} else if (navigator.getUserMedia){// 旧版APIstreamPromise await navigator.getUserMedia(videoConfig)}return streamPromise
}
/** 是否支持录音* returns 支持直接返回录音类实例 : 返回false/
HZRecorder.ready async function() {let instance // 录音类实例ready ok | false ready noawait HZRecorder.compatibleMedia({ audio: true }).then(stream {instance new HZRecorder(stream);}).catch(() {instance false})return instance
}
4、表单模式下的录音类
和上述流模式的录音类有区别的是表单模式下适用于上传录音时Content-Type为application/x-www-form-urlencoded噢~
// ————————————————–/** 录音类指定content-type为application/x-www-form-urlencoded使用* param {} stream 流对象/const HZRecorderForm function (stream) {//创建一个音频环境对象 audioContext window.AudioContext || window.webkitAudioContext;var ac new audioContext();var chunks [];var mediaRecordervar blobResult//开始录音 this.start function () {if (!mediaRecorder) {var origin ac.createMediaStreamSource(stream)var dest ac.createMediaStreamDestination();mediaRecorder new MediaRecorder(dest.stream);mediaRecorder.ondataavailable function(e) {chunks.push(e.data);}mediaRecorder.onstop function(evt) {blobResult new Blob(chunks, { type : audio/mpeg });};origin.connect(dest);}mediaRecorder.start();};// 结束录音this.end function () {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.requestData()mediaRecorder.stop();};// 暂停录音this.stop function() {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.pause()}// 恢复录音this.again function() {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.resume()}//上传 this.upload function (url, succ, err) {setTimeout(() {var xhr new XMLHttpRequest();xhr.open(POST, url);xhr.setRequestHeader(Content-Type, application/x-www-form-urlencoded)xhr.send(blobResult);xhr.onload e {// 请求完成 外部状态码200 内部状态码1这个内部状态码自定义if (xhr.readyState 4 xhr.status 200 JSON.parse(xhr.response).status 1) {succ succ(xhr.response)} else {err err(JSON.parse(xhr.response).message)}}})};}/*** 多浏览器兼容* param {} videoConfig 参数配置 param {} succ 成功回调 param {} fail 失败回调 returns promise/
HZRecorderForm.compatibleMedia async function(videoConfig) {let streamPromise // 视频promiseif (navigator.mediaDevices navigator.mediaDevices.getUserMedia){// 最新标准APIstreamPromise await navigator.mediaDevices.getUserMedia(videoConfig)} else if (navigator.webkitGetUserMedia){// webkit内核浏览器streamPromise await navigator.webkitGetUserMedia(videoConfig)} else if (navigator.mozGetUserMedia){// Firefox浏览器streamPromise await navagator.mozGetUserMedia(videoConfig)} else if (navigator.getUserMedia){// 旧版APIstreamPromise await navigator.getUserMedia(videoConfig)}return streamPromise
}
/** 是否支持录音* returns 支持直接返回录音类实例 : 返回false/
HZRecorderForm.ready async function() {let instance // 录音类实例ready ok | false ready noawait HZRecorderForm.compatibleMedia({ audio: true }).then(stream {instance new HZRecorderForm(stream);}).catch(() {instance false})return instance
}
5、疑难解答
1、在录音开始前都必须调用readyRecording方法吗
必须噢你也可以自己实现这功能HZRecorder.ready()方法返回的是promise对象其值在当前有麦克风时候返回的是录音类实例你拿到此值就可以调用录音类的方法无麦克风时候返回的是false表示当前不具备录音环境~
/** 录音前准备 检查录音设备是否到位/this.readyRecording async function() {let recorder // 表示录音类实例// 流模式下ready钩子 res 为录音类实例 或者 falseawait HZRecorder.ready().then(res {recorder res})// 表单模式下ready钩子 res 为录音类实例 或者 falseawait HZRecorderForm.ready().then(res {recorder res})return recorder}
2、火狐浏览器提示 navigator.mediaDevices is undefined找不到
是的噢火狐浏览器的navigator对象没有mediaDevices这个属性所以这也是我为啥在录音类里要加入compatibleMedia方法此方法就是用来兼容各个浏览器的噢~火狐就是用的navigator.mozGetUserMedia方法
/** 多浏览器兼容* param {} videoConfig 参数配置 param {} succ 成功回调 param {} fail 失败回调 returns promise/
HZRecorder.compatibleMedia async function(videoConfig) {let streamPromise // 视频promiseif (navigator.mediaDevices navigator.mediaDevices.getUserMedia){// 最新标准APIstreamPromise await navigator.mediaDevices.getUserMedia(videoConfig)} else if (navigator.webkitGetUserMedia){// webkit内核浏览器streamPromise await navigator.webkitGetUserMedia(videoConfig)} else if (navigator.mozGetUserMedia){// Firefox浏览器streamPromise await navagator.mozGetUserMedia(videoConfig)} else if (navigator.getUserMedia){// 旧版APIstreamPromise await navigator.getUserMedia(videoConfig)}return streamPromise
}
3、录音类的ready方法为啥要使用async/await呢
这个就有点涉及异步的知识了在一个异步函数里return是属于同步逻辑噢promise.then属于异步所以 return 会先于 .then执行的噢这就和我们的想法不一致了所以要await 阻塞代码拿到instance值了再返回
/** 是否支持录音* returns 支持直接返回录音类实例 : 返回false*/
HZRecorder.ready async function() {let instance // 录音类实例ready ok | false ready noawait HZRecorder.compatibleMedia({ audio: true }).then(stream {instance new HZRecorder(stream);}).catch(() {instance false})return instance
}
4、录音类为啥要使用 XMLHttpRequest 来触发接口呢
不一定要使用原生xhr噢你也可以根据你需求来修改成axios/fetch/ajax等~这个不影响整体代码的使用
5、流模式和表单模式的录音类本质上有啥区别
其实在外层是上传接口的请求头区别但在实际上只是由于流模式下的写法无法将音频转成mp3格式默认为wav格式当然网上也有小伙伴认为引入lame库来实现wav转换mp3的操作当然可以啦这不影响只是对我来说我是能不引入第三方库就不引入。
而表单模式实际上用的浏览器支持的另一个接口 MediaRecorder
而MediaRecorder是专门来做录制的他想转换格式的话就简单的多在录制完触发onstop时将可以将二进制数据转换成任意想要的格式audio/mpeg就是mp3的格式 mediaRecorder.ondataavailable function(e) {chunks.push(e.data);}mediaRecorder.onstop function(evt) {blobResult new Blob(chunks, { type : audio/mpeg });};
6、表单模式下的录音类为啥要判断inactive状态呢
因为 MediaRecorder 接口 有三个状态 inactive, recording, paused
这三个状态分别是设备闲置设备使用设备暂停有点类似于window的未响应当我们想要操作麦克风时此时麦克风inactive了那就无法响应我们的请求所以当状态为inactive时我们都return掉使他不执行我们的方法。 // 结束录音this.end function () {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.requestData()mediaRecorder.stop();};// 暂停录音this.stop function() {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.pause()}// 恢复录音this.again function() {// 当录音类处于不活跃状态时停止操作if (mediaRecorder.state inactive) returnmediaRecorder.resume()}
——有不懂的可以评论区聊聊噢~——
相关文章
-
天津做网站推广的网站小说网站开发猪八戒
天津做网站推广的网站小说网站开发猪八戒
- 技术栈
- 2026年03月21日
-
天津做网站的公司怎么样重庆市建设信息网站
天津做网站的公司怎么样重庆市建设信息网站
- 技术栈
- 2026年03月21日
-
天津做家政的网站做网站端口映射
天津做家政的网站做网站端口映射
- 技术栈
- 2026年03月21日
-
天猫网站建设的意义hao123上网主页官网设置成主页
天猫网站建设的意义hao123上网主页官网设置成主页
- 技术栈
- 2026年03月21日
-
天猫网站左侧菜单向右滑出的导航菜单二手车东莞网站建设
天猫网站左侧菜单向右滑出的导航菜单二手车东莞网站建设
- 技术栈
- 2026年03月21日
-
天门建设局官方网站用c 做毕业设计的音乐网站
天门建设局官方网站用c 做毕业设计的音乐网站
- 技术栈
- 2026年03月21日
