Web RTC + audio API 实现录音,并压缩
<button onclick="record()">开始录音</button>
<button onclick="stopRecord()">结束录音</button>
<!-- <button onclick="resetRecord()">重置录音</button> -->
<audio class="audio-node" id="audio" autoplay></audio>
<script>
class Recoder {
constructor (sampleRate) {
this.leftDataList = []
this.rightDataList = []
this.mediaPlayer = null
this.audioContext = null
this.source = null
this.sampleRate = sampleRate || 44100
}
startRecord () {
return new Promise((resolve, reject) => {
window.navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 8000, // 采样率
channelCount: 1, // 声道
audioBitsPerSecond : 64,
volume: 1.0, // 音量
autoGainControl: true
}
}).then(mediaStream => {
console.log(mediaStream,'mediaStream')
this.mediaPlayer = mediaStream
this.beginRecord(mediaStream)
resolve()
}).catch(err => {
// 如果用户电脑没有麦克风设备或者用户拒绝了,或者连接出问题了等
// 这里都会抛异常,并且通过err.name可以知道是哪种类型的错误
console.error(err)
reject(err)
})
})
} beginRecord (mediaStream) {
let audioContext = new (window.AudioContext || window.webkitAudioContext)()
// mediaNode包含 mediaStream,audioContext
let mediaNode = audioContext.createMediaStreamSource(mediaStream)
console.log(mediaNode,'mediaNode')
// 创建一个jsNode
// audioContext.sampleRate = 8000
console.log(audioContext,'audioContext')
let jsNode = this.createJSNode(audioContext)
console.log(jsNode,'jsnode')
// 需要连到扬声器消费掉outputBuffer,process回调才能触发
// 并且由于不给outputBuffer设置内容,所以扬声器不会播放出声音
jsNode.connect(audioContext.destination)
jsNode.onaudioprocess = this.onAudioProcess.bind(this)
// 把mediaNode连接到jsNode
mediaNode.connect(jsNode)
this.audioContext = audioContext
} onAudioProcess (event) {
console.log('is recording')
// 拿到输入buffer Float32Array
let audioBuffer = event.inputBuffer
let leftChannelData = audioBuffer.getChannelData(0)
// let rightChannelData = audioBuffer.getChannelData(1) // 需要克隆一下
this.leftDataList.push(leftChannelData.slice(0))
//this.rightDataList.push(rightChannelData.slice(0))
} createJSNode (audioContext) {
const BUFFER_SIZE = 4096
const INPUT_CHANNEL_COUNT = 1
const OUTPUT_CHANNEL_COUNT = 1
// createJavaScriptNode已被废弃
let creator = audioContext.createScriptProcessor || audioContext.createJavaScriptNode
creator = creator.bind(audioContext)
return creator(BUFFER_SIZE, INPUT_CHANNEL_COUNT, OUTPUT_CHANNEL_COUNT)
} playRecord (arrayBuffer) {
let blob = new Blob([new Int8Array(arrayBuffer)], {
type: 'audio/mp3' // files[0].type
})
let blobUrl = URL.createObjectURL(blob)
this.source = blob
this.blobUrl = blobUrl
// document.querySelector('.audio-node').src = blobUrl
return blobUrl
} stopRecord () {
// 停止录音
let leftData = this.mergeArray(this.leftDataList)
//let rightData = this.mergeArray(this.rightDataList)
let allData = this.interSingleData(leftData)
let wavBuffer = this.createWavFile(allData) let source = this.playRecord(wavBuffer)
this.resetRecord()
return source
} transformArrayBufferToBase64 (buffer) {
var binary = ''
var bytes = new Uint8Array(buffer)
for (var len = bytes.byteLength, i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i])
}
return window.btoa(binary)
} // 停止控件录音
resetRecord () {
this.leftDataList = []
this.rightDataList = []
this.audioContext.close()
this.mediaPlayer.getAudioTracks().forEach(track => {
track.stop()
this.mediaPlayer.removeTrack(track)
})
} createWavFile (audioData) {
let channelCount = 1
const WAV_HEAD_SIZE = 44
const sampleBits = 16
let sampleRate = this.sampleRate let buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE)
// 需要用一个view来操控buffer
let view = new DataView(buffer)
// 写入wav头部信息
// RIFF chunk descriptor/identifier
this.writeUTFBytes(view, 0, 'RIFF')
// RIFF chunk length
view.setUint32(4, 44 + audioData.length * channelCount, true)
// RIFF type
this.writeUTFBytes(view, 8, 'WAVE')
// format chunk identifier
// FMT sub-chunk
this.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, channelCount, 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, 2 * 2, true)
// bits per sample
view.setUint16(34, 16, true)
// data sub-chunk
// data chunk identifier
this.writeUTFBytes(view, 36, 'data')
// data chunk length
view.setUint32(40, audioData.length * 2, true) console.log(view,'view')
let length = audioData.length
let index = 44
let volume = 1
for (let i = 0; i < length; i++) {
view.setInt16(index, audioData[i] * (0x7FFF * volume), true)
index += 2
}
return buffer
} writeUTFBytes (view, offset, string) {
var lng = string.length
for (var i = 0; i < lng; i++) {
view.setUint8(offset + i, string.charCodeAt(i))
}
} interSingleData (left) {
var t = left.length;
let sampleRate = this.audioContext.sampleRate,
outputSampleRate = this.sampleRate
sampleRate += 0.0;
outputSampleRate += 0.0;
var s = 0,
o = sampleRate / outputSampleRate,
u = Math.ceil(t * outputSampleRate / sampleRate),
a = new Float32Array(u);
for (let i = 0; i < u; i++) {
a[i] = left[Math.floor(s)];
s += o;
}
return a;
} // 交叉合并左右声道的数据
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
} mergeArray (list) {
let length = list.length * list[0].length
let 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
} // 播放音乐
playMusic () {
if (!this.value) {
return
}
// 直接使用File对象生成blob url
let blobUrl = URL.createObjectURL(this.files[0])
document.querySelector('.audio-node').src = blobUrl
} play (arrayBuffer) {
// Safari需要使用webkit前缀
let AudioContext = this.AudioContext || this.webkitAudioContext
let audioContext = new AudioContext()
// 创建一个AudioBufferSourceNode对象,使用AudioContext的工厂函数创建
let audioNode = audioContext.createBufferSource()
// 解码音频,可以使用Promise,但是较老的Safari需要使用回调
audioContext.decodeAudioData(arrayBuffer, function (audioBuffer) {
audioNode.buffer = audioBuffer
audioNode.connect(audioContext.destination)
// 从0s开始播放
audioNode.start(0)
})
}
}
let recoder = new Recoder(8000)
function record() {
recoder.startRecord()
} function stopRecord(params) {
recoder.stopRecord()
let source = recoder.source
let formData = new FormData()
formData.append('audio', source)
let audio = document.getElementById('audio')
audio.src = recoder.blobUrl
}
</script>
参考 自掘金
1.网上很多都没有停之功能,新增停之
2.这里用的8000采样率和单声道,音频体积为原来的 快 1/12 (我电脑设备的采样录是44K)
3.详细解释请看 H5音频分析
Web RTC + audio API 实现录音,并压缩的更多相关文章
- 【HTML5】Web Audio API打造超炫的音乐可视化效果
HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我! 源码已经挂到github上了,有兴趣的同学也可以去st ...
- H5的Web Audio Api
概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...
- Web Audio API之手把手教你用web api处理声音信号:可视化音乐demo
1.Web Audio API 介绍 Web Audio API 提供了在Web上控制音频的一个非常有效通用的系统 ,这些通用系统通俗的讲就是我们可以利用Web Audio API提供的各种方法操作各 ...
- 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况
对比audio标签 和 Web Audio API 各平台浏览器的支持情况: audio element Web Audio API desktop browsers Chrome 14 Yes ...
- [Javascript] Intro to the Web Audio API
An introduction to the Web Audio API. In this lesson, we cover creating an audio context and an osci ...
- 关于Web Audio API的入门
Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...
- 使用Web Audio API绘制音波图
摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...
- HTML5 ——web audio API 音乐可视化(二)
上一篇 web audio API 音乐可视化(一)介绍了一些基本的API,以及如何简单的播放一个音频,本篇介绍一下怎么对获取到的音频进行分析,并将分析后的数据绘制成图像. 最终效果请戳这里; 完整版 ...
- HTML5 ——web audio API 音乐可视化(一)
使用Web Audio API可以对音频进行分析和操作,最终实现一个音频可视化程序. 最终效果请戳这里; 完整版代码请戳这里,如果还看得过眼,请给一个start⭐ 一.API AudioContext ...
随机推荐
- 记录MNIST采用卷积方式实现与理解
从时间上来说,这篇文章写的完了,因为这个实验早就做完了:但从能力上来说,这篇文章出现的早了,因为很多地方我都还没有理解.如果不现在写,不知道什么时候会有时间是其一,另外一个原因是怕自己过段时间忘记. ...
- linux文本查看与搜索
1. cat-->全文本显示 cat file #全文本显示在终端 cat -n file #显示全文本,并显示行号 cat file1 file2 >file3 #将file1 file ...
- git 忽略提交及已push过得文件忽略提交
在使用Git的过程中,我们喜欢有的文件比如日志,临时文件,编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交 Git 忽略文件提交的方法 这种方式通过在项目的某个文件 ...
- HDU 5183 Negative and Positive (NP) (手写哈希)
题目链接:HDU 5183 Problem Description When given an array \((a_0,a_1,a_2,⋯a_{n−1})\) and an integer \(K\ ...
- android ndk 编译 libevent
1. 下载 libevent 2.1.8 版本 https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/ ...
- C++中函数模板的深入理解
1,函数模板深入理解: 1,编译器从函数模板通过具体类型产生不同的函数: 1,模板就是模子,通过这个模子可以产生很多的实物: 2,函数模板就是编译器用来产生具体函数的模子: 2,编译器会对函数模板进行 ...
- java虚拟机规范(se8)——class文件格式(七)
4.7.5 Exceptions 属性 Exceptions 属性是一个变长属性,它位于 method_info(§4.6)结构的属性表中. Exceptions 属性指出了一个方法需要检查的可能抛出 ...
- linux装mysql
1.对于MySQL-5.6.35-1.el6.x86_64.rpm-bundle.tar来说 2.命令: rpm -Uvh MySQL-*.rpm 3.将my.cnf复制到/etc下 4.记得关掉se ...
- 修改bug 提交出错:操作失败: 无法更改关系,因为一个或多个外键属性不可以为 null
提交出错:操作失败: 无法更改关系,因为一个或多个外键属性不可以为 null.对关系作出更改后,会将相关的外键属性设置为 null 值.如果外键不支持 null 值,则必须定义新的关系,必须向外键属性 ...
- Failed to resolve com.android.support:support-compat:25.4.0
3down votefavorite I am trying to include this library to my project by adding compile 'jp.wasabee ...