<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="apple-mobile-web-capable" content="yes">
<title>WEB录音机</title>
</head>
<body>
<button onclick="test66.record()">开始录音</button>
<button onclick="test66.stopRecord()">停止录音</button>
<button onclick="test66.bofangRecord()">播放录音</button>
<button onclick="test66.recordClose()">关闭录音</button>
<audio class="audio-node" controls autoplay></audio>
</body>
<script type="text/javascript">
const recordObj = class record {
constructor() {
console.log('new了')
this.recordData = {
mediaStream: null,
mediaNode: null,
jsNode: null
};
this.leftDataList = [];
this.rightDataList = [];
console.log(this.recordData, 'this.recordData', this.leftDataList, 'this.leftDataList', this.rightDataList, 'this.rightDataList')
}
record() {
// console.log(this.recordData, 'this.recordData', this.leftDataList, 'this.leftDataList', this.rightDataList, 'this.rightDataList')
window.navigator.mediaDevices.getUserMedia({
audio: {
sampleRate: 44100, // 采样率
channelCount: 2, // 声道
volume: 2.0 // 音量
}
}).then(mediaStream => {
console.log(mediaStream);// 这里可以拿到mediaStream
this.recordData.mediaStream = mediaStream
this.beginRecord(this.recordData.mediaStream);
}).catch(err => {
// 如果用户电脑没有麦克风设备或者用户拒绝了,或者连接出问题了等
// 这里都会抛异常,并且通过err.name可以知道是哪种类型的错误
console.error(err);
});
}
beginRecord(mediaStream) {
let audioContext = new (window.AudioContext || window.webkitAudioContext);
let mediaNode = audioContext.createMediaStreamSource(mediaStream);
console.log(mediaNode)
this.recordData.mediaNode = mediaNode
// 这里connect之后就会自动播放了
// mediaNode.connect(audioContext.destination); //直接把录的音直接播放出来
// 创建一个jsNode
let jsNode = this.createJSNode(audioContext);
this.recordData.jsNode = jsNode
console.log(this.leftDataList,'this.leftDataList beginRecord')
// 需要连到扬声器消费掉outputBuffer,process回调才能触发
// 并且由于不给outputBuffer设置内容,所以扬声器不会播放出声音
jsNode.connect(audioContext.destination);
jsNode.onaudioprocess = (event) => {
this.onAudioProcess(event);
}
// 把mediaNode连接到jsNode
mediaNode.connect(jsNode);
}
createJSNode(audioContext) {
const BUFFER_SIZE = 4096; //4096
const INPUT_CHANNEL_COUNT = 2;
const OUTPUT_CHANNEL_COUNT = 2;
// createJavaScriptNode已被废弃
let creator = audioContext.createScriptProcessor || audioContext.createJavaScriptNode;
creator = creator.bind(audioContext);
return creator(BUFFER_SIZE,
INPUT_CHANNEL_COUNT, OUTPUT_CHANNEL_COUNT);
}
onAudioProcess(event) {
// console.log(event.inputBuffer);
let audioBuffer = event.inputBuffer;
let leftChannelData = audioBuffer.getChannelData(0),
rightChannelData = audioBuffer.getChannelData(1);
console.log(leftChannelData, rightChannelData,this.leftDataList,'this.leftDataList');
// 需要克隆一下
this.leftDataList.push(leftChannelData.slice(0));
this.rightDataList.push(rightChannelData.slice(0));
}
bofangRecord() {
// 播放录音
let leftData = this.mergeArray(this.leftDataList),
rightData = this.mergeArray(this.rightDataList);
if (leftData == null || leftData == null) {
alert("请先录音再播放");
return;
}
let allData = this.interleaveLeftAndRight(leftData, rightData);
let wavBuffer = this.createWavFile(allData);
this.playRecord(wavBuffer);
}
playRecord(arrayBuffer) {
let blob = new Blob([new Uint8Array(arrayBuffer)]);
let blobUrl = URL.createObjectURL(blob);
document.querySelector('.audio-node').src = blobUrl;
// 增加了下载功能
const aLink = document.createElement('a')
aLink.href = blobUrl
// 文件名字
aLink.download = '测试数据.mp3'
document.body.appendChild(aLink)
aLink.click()
aLink.remove();
}
stopRecord() {
// 停止录音
this.recordData.mediaNode.disconnect();
this.recordData.jsNode.disconnect();
console.log("已停止录音")
// console.log(this.leftDataList, this.rightDataList);
}
recordClose() {
if (this.recordData.mediaStream == null || this.recordData.mediaStream.getAudioTracks() == null || this.recordData.mediaStream.getAudioTracks().length == 0) {
return;
}
// 停止语音
this.recordData.mediaStream.getAudioTracks()[0].stop();
console.log("已停止语音")
}
mergeArray(list) {
if (list == null || list.length == 0 || list[0] == null || list[0].length == 0) {
return null;
}
let length = list.length * list[0].length;
let data = new Float32Array(length),
offset = 0;
for (let i = 0; i < list.length; i++) {
data.set(list[i], offset);
offset += list[i].length;
}
return data;
}
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;
}
createWavFile(audioData) {// audioData是合并后的数据
const WAV_HEAD_SIZE = 44;
let buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE),
// 需要用一个view来操控buffer
view = new DataView(buffer);
// 写入wav头部信息
// RIFF chunk descriptor/identifier
this.writeUTFBytes(view, 0, 'RIFF');
// RIFF chunk length
view.setUint32(4, 44 + audioData.length * 2, 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, 2, true);
// sample rate
view.setUint32(24, 44100, true);
// byte rate (sample rate * block align)
view.setUint32(28, 44100 * 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);
// 写入wav头部,代码同上
// 写入PCM数据
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));
}
}
}
const test66= new recordObj()
</script>
</html>
录音的类 复制即用 解决录音文件渲染没显示总时长问题
最新推荐文章于 2024-06-02 21:38:16 发布