设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 我们致力于打造专业的站长资讯、交流、合作平台!
热搜: 曝光 手机 公司
当前位置: 首页 > 站长学院 > MySql教程 > 正文

HTML5录音实践总结(Preact)

发布时间:2020-05-10 12:15 所属栏目:[MySql教程] 来源:站长网
导读:副标题#e# ArrayBuffer 转 Base64 PCM 文件播放 重采样 PCM 转 MP3 PCM 转 WAV 短时能量计算 Web Worker优化性能 音频存储(IndexedDB) WebView 开启 WebRTC 获取 PCM 数据 查看 DEMO https://github.com/deepkolos/pc-pcm-wave 样例代码: const mediaStream

ArrayBuffer 转 Base64

PCM 文件播放

重采样

PCM 转 MP3

PCM 转 WAV

短时能量计算

Web Worker优化性能

音频存储(IndexedDB)

WebView 开启 WebRTC

获取 PCM 数据

查看 DEMO

https://github.com/deepkolos/pc-pcm-wave

样例代码:

const mediaStream = await window.navigator.mediaDevices.getUserMedia({ audio: { // sampleRate: 44100, // 采样率 不生效需要手动重采样 channelCount: 1, // 声道 // echoCancellation: true, // noiseSuppression: true, // 降噪 实测效果不错 }, }) const audioContext = new window.AudioContext() const inputSampleRate = audioContext.sampleRate const mediaNode = audioContext.createMediaStreamSource(mediaStream) if (!audioContext.createScriptProcessor) { audioContext.createScriptProcessor = audioContext.createJavaScriptNode } // 创建一个jsNode const jsNode = audioContext.createScriptProcessor(4096, 1, 1) jsNode.connect(audioContext.destination) jsNode.onaudioprocess = (e) => { // e.inputBuffer.getChannelData(0) (left) // 双通道通过e.inputBuffer.getChannelData(1)获取 (right) } mediaNode.connect(jsNode)

简要流程如下:

start=>start: 开始 getUserMedia=>operation: 获取MediaStream audioContext=>operation: 创建AudioContext scriptNode=>operation: 创建scriptNode并关联AudioContext onaudioprocess=>operation: 设置onaudioprocess并处理数据 end=>end: 结束 start->getUserMedia->audioContext->scriptNode->onaudioprocess->end

停止录制只需要把 audioContext 挂在的 node 卸载即可,然后把存储的每一帧数据合并即可产出 PCM 数据

jsNode.disconnect() mediaNode.disconnect() jsNode.onaudioprocess = null

PCM 数据处理

通过 WebRTC 获取的 PCM 数据格式是 Float32 的, 如果是双通道录音的话, 还需要增加合并通道

const leftDataList = []; const rightDataList = []; function onAudioProcess(event) { // 一帧的音频PCM数据 let audioBuffer = event.inputBuffer; leftDataList.push(audioBuffer.getChannelData(0).slice(0)); rightDataList.push(audioBuffer.getChannelData(1).slice(0)); } // 交叉合并左右声道的数据 function interleaveLeftAndRight(left, right) { let totalLength = left.length + right.length; let data = new Float32Array(totalLength); for (let i = 0; i < left.length; i++) { let k = i * 2; data[k] = left[i]; data[k + 1] = right[i]; } return data; }

Float32 转 Int16

const float32 = new Float32Array(1) const int16 = Int16Array.from( float32.map(x => (x > 0 ? x * 0x7fff : x * 0x8000)), )

arrayBuffer 转 Base64

注意: 在浏览器上有个 btoa() 函数也是可以转换为 Base64 但是输入参数必须为字符串, 如果传递 buffer 参数会先被 toString() 然后再 Base64 , 使用 ffplay 播放反序列化的 Base64 , 会比较刺耳

使用 base64-arraybuffer 即可完成

import { encode } from 'base64-arraybuffer' const float32 = new Float32Array(1) const int16 = Int16Array.from( float32.map(x => (x > 0 ? x * 0x7fff : x * 0x8000)), ) console.log(encode(int16.buffer))

验证 Base64 是否正确, 可以在 node 下把产出的 Base64 转换为 Int16 的 PCM 文件, 然后使用 FFPlay 播放, 看看音频是否正常播放

PCM 文件播放

# 单通道 采样率:16000 Int16 ffplay -f s16le -ar 16k -ac 1 test.pcm # 双通道 采样率:48000 Float32 ffplay -f f32le -ar 48000 -ac 2 test.pcm

重采样/调整采样率

虽然 getUserMedia 参数可设置采样率, 但是在最新Chrome也不生效, 所以需要手动做个重采样

const mediaStream = await window.navigator.mediaDevices.getUserMedia({ audio: { // sampleRate: 44100, // 采样率 设置不生效 channelCount: 1, // 声道 // echoCancellation: true, // 减低回音 // noiseSuppression: true, // 降噪, 实测效果不错 }, })

使用 wave-resampler 即可完成

import { resample } from 'wave-resampler' const inputSampleRate = 44100 const outputSampleRate = 16000 const resampledBuffers = resample( // 需要onAudioProcess每一帧的buffer合并后的数组 mergeArray(audioBuffers), inputSampleRate, outputSampleRate, )

PCM 转 MP3

import { Mp3Encoder } from 'lamejs' let mp3buf const mp3Data = [] const sampleBlockSize = 576 * 10 // 工作缓存区, 576的倍数 const mp3Encoder = new Mp3Encoder(1, outputSampleRate, kbps) const samples = float32ToInt16( audioBuffers, inputSampleRate, outputSampleRate, ) let remaining = samples.length for (let i = 0; remaining >= 0; i += sampleBlockSize) { const left = samples.subarray(i, i + sampleBlockSize) mp3buf = mp3Encoder.encodeBuffer(left) mp3Data.push(new Int8Array(mp3buf)) remaining -= sampleBlockSize } mp3Data.push(new Int8Array(mp3Encoder.flush())) console.log(mp3Data) // 工具函数 function float32ToInt16(audioBuffers, inputSampleRate, outputSampleRate) { const float32 = resample( // 需要onAudioProcess每一帧的buffer合并后的数组 mergeArray(audioBuffers), inputSampleRate, outputSampleRate, ) const int16 = Int16Array.from( float32.map(x => (x > 0 ? x * 0x7fff : x * 0x8000)), ) return int16 }

使用 lamejs 即可, 但是体积较大(160+KB), 如果没有存储需求可使用 WAV 格式

(编辑:ASP站长网)

网友评论
推荐文章
随机内容