<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. 解决myeclipse validation验证javascript导致速度变慢的现象

    参考:https://jingyan.baidu.com/article/ca41422fe094251eae99ede7.html

  2. 搭建git服务器遇到的问题

    1.错误提示: remote: error: insufficient permission for adding an object to repository database ./objects ...

  3. React-Native实战项目-导航器篇(一)

    前言:官方文档已经看了一遍,但印象不是很深,于是在mooc上找了个实战学习项目做一做. 本篇目录: 基础导航练习√ 1.ReactNavigation之createStackNavigator导航器案 ...

  4. IntelliJ Idea 环境搭建

    安装IntelliJ Idea 可以去官网下载: https://www.jetbrains.com/idea/download/#section=windows 选择Ultimate.一路下一步安装 ...

  5. go语言从例子开始之Example6.if/else

    if 和 else 分支结构在 Go 中当然是直接了当的了. package main import "fmt" func main() { 这里是一个基本的例子. if 7%2 ...

  6. centos7 iperf3安装

    iperf3快速安装 wget -O /usr/lib/libiperf.so.0 https://iperf.fr/download/ubuntu/libiperf.so.0_3.1.3 wget ...

  7. MyISAM和InnoDB引擎的区别

    MySQL默认采用的是MyISAM. MyISAM不支持事务,而InnoDB支持.InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事务,自动提交,这样会影响速度,所以 ...

  8. iSkysoft iMedia Converter Deluxe Mac如何制作视频?视频格式转换工具制作动图的方法

    使用iSkysoft iMedia Converter Deluxe Mac如何制作视频?使用视频格式转换工具,你可以轻松进行动图或视频的制作,也可以把你喜欢的视频的某一段提取出来,制作成你自己风格的 ...

  9. 如何在Mac上将视频刻录到DVD / ISO文件

    如果您希望将喜爱的视频转换为DVD / Blu-ray光盘以进行物理备份或播放,则Mac版Wondershare UniConverter可以专业地完成任务.今天的教程就是如何在Mac上轻松刻录DVD ...

  10. Mac上使用频率最高的七组快捷键

    不管Mac还是Windows,会使用快捷键都能让你的工作效果提升不少,但是快捷键那么多一下子全部记下来也不太实际,这里小编就整理了七组使用频率最高的快捷键,希望可以帮到大家. https://www. ...