设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 创业者 数据 手机
当前位置: 首页 > 站长学院 > MySql教程 > 正文

HTML5录音实践总结(Preact)(3)

发布时间:2020-05-10 12:15 所属栏目:115 来源:站长网
导读:ls -alh-rwxrwxrwx 1 root root 95K 4月 22 12:45 12s.mp3*-rwxrwxrwx 1 root root 1.1M 4月 22 12:44 12s.wav*-rwxrwxrwx 1 root root 235K 4月 22 12:41 30s.mp3*-rwxrwxrwx 1 root root 2.6M 4月 22 12:40 30s.w

> ls -alh -rwxrwxrwx 1 root root 95K 4月 22 12:45 12s.mp3* -rwxrwxrwx 1 root root 1.1M 4月 22 12:44 12s.wav* -rwxrwxrwx 1 root root 235K 4月 22 12:41 30s.mp3* -rwxrwxrwx 1 root root 2.6M 4月 22 12:40 30s.wav* -rwxrwxrwx 1 root root 63K 4月 22 12:49 8s.mp3* -rwxrwxrwx 1 root root 689K 4月 22 12:48 8s.wav*

PCM 转 WAV

function mergeArray(list) { const length = list.length * list[0].length const data = new Float32Array(length) let offset = 0 for (let i = 0; i < list.length; i++) { data.set(list[i], offset) offset += list[i].length } return data } function writeUTFBytes(view, offset, string) { var lng = string.length for (let i = 0; i < lng; i++) { view.setUint8(offset + i, string.charCodeAt(i)) } } function createWavBuffer(audioData, sampleRate = 44100, channels = 1) { const WAV_HEAD_SIZE = 44 const buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE) // 需要用一个view来操控buffer const view = new DataView(buffer) // 写入wav头部信息 // RIFF chunk descriptor/identifier writeUTFBytes(view, 0, 'RIFF') // RIFF chunk length view.setUint32(4, 44 + audioData.length * 2, true) // RIFF type writeUTFBytes(view, 8, 'WAVE') // format chunk identifier // FMT sub-chunk writeUTFBytes(view, 12, 'fmt') // format chunk length view.setUint32(16, 16, true) // sample format (raw) view.setUint16(20, 1, true) // stereo (2 channels) view.setUint16(22, channels, true) // sample rate view.setUint32(24, sampleRate, true) // byte rate (sample rate * block align) view.setUint32(28, sampleRate * 2, true) // block align (channel count * bytes per sample) view.setUint16(32, channels * 2, true) // bits per sample view.setUint16(34, 16, true) // data sub-chunk // data chunk identifier writeUTFBytes(view, 36, 'data') // data chunk length view.setUint32(40, audioData.length * 2, true) // 写入PCM数据 let index = 44 const volume = 1 const { length } = audioData for (let i = 0; i < length; i++) { view.setInt16(index, audioData[i] * (0x7fff * volume), true) index += 2 } return buffer } // 需要onAudioProcess每一帧的buffer合并后的数组 createWavBuffer(mergeArray(audioBuffers))

WAV 基本上是 PCM 加上一些音频信息

简单的短时能量计算

function shortTimeEnergy(audioData) { let sum = 0 const energy = [] const { length } = audioData for (let i = 0; i < length; i++) { sum += audioData[i] ** 2 if ((i + 1) % 256 === 0) { energy.push(sum) sum = 0 } else if (i === length - 1) { energy.push(sum) } } return energy }

由于计算结果有会因设备的录音增益差异较大, 计算出数据也较大, 所以使用比值简单区分人声和噪音

查看 DEMO

const NoiseVoiceWatershedWave = 2.3 const energy = shortTimeEnergy(e.inputBuffer.getChannelData(0).slice(0)) const avg = energy.reduce((a, b) => a + b) / energy.length const nextState = Math.max(...energy) / avg > NoiseVoiceWatershedWave ? 'voice' : 'noise'

Web Worker 优化性能

音频数据数据量较大, 所以可以使用 Web Worker 进行优化, 不卡 UI 线程

在 Webpack 项目里 Web Worker 比较简单, 安装 worker-loader 即可

preact.config.js

export default (config, env, helpers) => { config.module.rules.push({ test: /\.worker\.js$/, use: { loader: 'worker-loader', options: { inline: true } }, }) }

recorder.worker.js

self.addEventListener('message', event => { console.log(event.data) // 转MP3/转Base64/转WAV等等 const output = '' self.postMessage(output) }

使用 Worker

async function toMP3(audioBuffers, inputSampleRate, outputSampleRate = 16000) { const { default: Worker } = await import('./recorder.worker') const worker = new Worker() // 简单使用, 项目可以在recorder实例化的时候创建worker实例, 有并法需求可多个实例 return new Promise(resolve => { worker.postMessage({ audioBuffers: audioBuffers, inputSampleRate: inputSampleRate, outputSampleRate: outputSampleRate, type: 'mp3', }) worker.onmessage = event => resolve(event.data) }) }

音频的存储

浏览器持久化储存的地方有 LocalStorage 和 IndexedDB , 其中 LocalStorage 较为常用, 但是只能储存字符串, 而 IndexedDB 可直接储存 Blob , 所以优先选择 IndexedDB ,使用 LocalStorage 则需要转 Base64 体积将会更大

所以为了避免占用用户太多空间, 所以选择MP3格式进行存储

> ls -alh -rwxrwxrwx 1 root root 95K 4月 22 12:45 12s.mp3* -rwxrwxrwx 1 root root 1.1M 4月 22 12:44 12s.wav* -rwxrwxrwx 1 root root 235K 4月 22 12:41 30s.mp3* -rwxrwxrwx 1 root root 2.6M 4月 22 12:40 30s.wav* -rwxrwxrwx 1 root root 63K 4月 22 12:49 8s.mp3* -rwxrwxrwx 1 root root 689K 4月 22 12:48 8s.wav*

IndexedDB 简单封装如下, 熟悉后台的同学可以找个 ORM 库方便数据读写

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读