<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 实现录音,并压缩的更多相关文章

  1. 【HTML5】Web Audio API打造超炫的音乐可视化效果

    HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我!  源码已经挂到github上了,有兴趣的同学也可以去st ...

  2. H5的Web Audio Api

    概述 研究Web Audio Api的主要原因是:工作中需要在ios中实现声音的淡出效果,主要是通过setInterval来改audio标签的volume属性实现的,但是ios上面volume属性是只 ...

  3. Web Audio API之手把手教你用web api处理声音信号:可视化音乐demo

    1.Web Audio API 介绍 Web Audio API 提供了在Web上控制音频的一个非常有效通用的系统 ,这些通用系统通俗的讲就是我们可以利用Web Audio API提供的各种方法操作各 ...

  4. 关于HTML5音频——audio标签和Web Audio API各平台浏览器的支持情况

    对比audio标签 和 Web Audio API 各平台浏览器的支持情况:   audio element Web Audio API desktop browsers Chrome 14 Yes  ...

  5. [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 ...

  6. 关于Web Audio API的入门

    Web Audio API提供了一个简单强大的机制来实现控制web应用程序的音频内容.它允许你开发复杂的混音,音效,平移以及更多. 可以先看一下MDN的这篇文章<Web Audio API的运用 ...

  7. 使用Web Audio API绘制音波图

    摘要:Web Audio API是对<audio> 标签功能上的补充,我们可以用它完成混音.音效.平移等各种复杂的音频处理,本文简单的使用其完成音波图的绘制. PS:本例子使用ES6编程, ...

  8. HTML5 ——web audio API 音乐可视化(二)

    上一篇 web audio API 音乐可视化(一)介绍了一些基本的API,以及如何简单的播放一个音频,本篇介绍一下怎么对获取到的音频进行分析,并将分析后的数据绘制成图像. 最终效果请戳这里; 完整版 ...

  9. HTML5 ——web audio API 音乐可视化(一)

    使用Web Audio API可以对音频进行分析和操作,最终实现一个音频可视化程序. 最终效果请戳这里; 完整版代码请戳这里,如果还看得过眼,请给一个start⭐ 一.API AudioContext ...

随机推荐

  1. play framework 在idea简单运行配置(mac为例)

    文章目录 play 最基本的构建 在idea中配置 配置jdk相关 配置play 运行 运行 play 最基本的构建 https://blog.csdn.net/dataiyangu/article/ ...

  2. idea新手教程 如何springmvc创建Maven项目

    1.点击Create project,选择maven,勾选Create from archetype选择web-app,如图 2 输入    Group-Id     //组织名,也是作者名 Arti ...

  3. 浅析java设计模式(一)----异构容器,可以存储任何对象类型为其他类提供该对象

    最近在着手重构一个java UI桌面项目,发现这个项目在一开始的时候由于需求不明确,以及开发人员对swing框架不熟悉等问题造成了页面代码混乱的情况:为了能够在各个类里都可以拿到其他类的引用去进行相应 ...

  4. 整理eclipse,升级jdk环境小记录

    这2天在整理项目: 需要把eclipse 32位,jdk1.6 32位的更改为eclipse 64位,jdk1.8 64位版本的,于是我就在一台window7的电脑上直接操作,遇到了一下几点问题,记录 ...

  5. 蛋糕仙人的javascript笔记

    蛋糕仙人的javascript笔记:https://www.w3cschool.cn/kesyi/kesyi-nqej24rv.html

  6. json模块 pickle 模块 collections 模块 openpyxl 模块

    json模块 json 模块是一个系列化模块 一个第三方的特殊数据格式 可以将python数据类型----> json 数据格式 ----> 字符串 ----> 文件 其他语言想要使 ...

  7. Riverside Curio

    Riverside Curio time limit per test1 second memory limit per test256 megabytes Arkady decides to obs ...

  8. Mybatis+Springmvc+Spring整合常用的配置文件

    1.创建web项目 2.导入mabatis spring springnvc 需要的jar包 3.创建mybatis,spring,springmvc的配置文件 (1)web.xml配置文件 < ...

  9. js 动态绑定解绑事件

    function addEvent(obj, type, handle) { if (obj.addEventListener) { obj.addEventListener(type, handle ...

  10. 分支结构case 语句举例