<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>降噪</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<link rel="stylesheet" href="./css/index.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>
<div id="app">
<el-button @click="startRecording" :disabled="btn.start">开始录音</el-button>
<el-button @click="endRecording" :disabled="btn.end">结束录音</el-button>
</div>
<script src="./js/index-1.js"></script>
</body>
</html>
new Vue({
el: '#app',
data() {
return {
btn: {
start: false,
end: true
},
mediaData: []
}
},
methods: {
startRecording() {
let _this_ = this,
config = {
audio: {
channelCount: 1,
volume: 1.0,
noiseSuppression: false,
autoGainControl: false,
echoCancellation: false,
sampleRate: 48000,
sampleSize: 16
},
video: false
};
const getMediaSuccess = (stream) => {
_this_.btn = {
start: true,
end: false
}
_this_.stream = stream;
_this_.scriptProcessor = _this_.audioContext.createScriptProcessor(1024, 1, 1);
_this_.scriptProcessor.onaudioprocess = function (e) {
_this_.mediaData.push(new Float32Array(e.inputBuffer.getChannelData(0)));
};
_this_.mediaSource = _this_.audioContext.createMediaStreamSource(stream);
_this_.mediaSource.connect(_this_.scriptProcessor);
_this_.scriptProcessor.connect(_this_.audioContext.destination);
}
const getMediaFail = (e) => {
_this_.audioContext && _this_.audioContext.close();
_this_.audioContext = undefined;
_this_.btn = {
start: false,
end: true
}
switch (e.name || e.code) {
case 'PERMISSION_DENIED':
case 'PermissionDeniedError':
throw new Error('用户拒绝提供信息');
break
case 'NOT_SUPPORTED_ERROR':
case 'NotSupportedError':
throw new Error('浏览器不支持硬件设备');
break
case 'MANDATORY_UNSATISFIED_ERROR':
case 'MandatoryUnsatisfiedError':
throw new Error('无法发现指定硬件设备');
break
default:
throw new Error(`无法打开麦克风。异常信息 ${e.code} || ${e.name}`);
}
}
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
try {
_this_.audioContext = new (window.AudioContext || window.webkitAudioContext)({});
console.log('默认采样率:', _this_.audioContext.sampleRate);
_this_.sampleRate = _this_.audioContext.sampleRate;
if (_this_.sampleRate < 44100) {
_this_.$message.warning('当前设备支持采样率过低');
throw new Error('当前设备支持采样率过低');
}
if (!_this_.audioContext) {
_this_.$message.warning('webAudio Api 不支持当前浏览器');
throw new Error('webAudio Api 不支持当前浏览器');
}
} catch (e) {
if (!_this_.audioContext) {
throw new Error('webAudio Api 不支持当前浏览器');
}
}
if (navigator.mediaDevices && navigator.mediaDevices['getUserMedia']) {
navigator.mediaDevices.getUserMedia({
...config
}).then(function (stream) {
getMediaSuccess(stream);
}).catch(function (e) {
getMediaFail(e);
});
} else if (navigator.getUserMedia) {
navigator.getUserMedia({
...config
}, function (stream) {
getMediaSuccess(stream);
}, function (e) {
getMediaFail(e);
});
} else {
_this_.audioContext && _this_.audioContext.close();
if (navigator.userAgent.toLowerCase().match(/chrome/) && location.origin.indexOf('https://') < 0) {
throw new Error('chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限');
} else {
throw new Error('无法获取浏览器录音功能,请升级浏览器或使用chrome');
}
}
},
endRecording() {
if (this.stream && this.stream.getTracks) {
this.stream.getTracks().forEach((track) => {
return track.stop();
})
this.stream = null;
this.audioContext && this.audioContext.close();
this.scriptProcessor.disconnect();
let blob = new Blob([new Uint8Array(this.createWavFile(this.mergeArray(this.mediaData)))], { type: 'audio/wav' });
this.downloadFile(URL.createObjectURL(blob));
this.mediaData = [];
this.btn = {
start: false,
end: true
}
}
},
writeUTFBytes(view, offset, string) {
for (var i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
},
mergeArray(list) {
let length = list.length * list[0].length,
data = new Float32Array(length),
offset = 0;
for (var i = 0; i < list.length; i++) {
data.set(list[i], offset);
offset += list[i].length;
}
return data;
},
createWavFile(audioData) {
let buffer = new ArrayBuffer(audioData.length * 2 + 44),
view = new DataView(buffer);
this.writeUTFBytes(view, 0, 'RIFF');
view.setUint32(4, 36 + audioData.length * 2, true);
this.writeUTFBytes(view, 8, 'WAVE');
this.writeUTFBytes(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true);
view.setUint16(22, 1, true);
view.setUint32(24, this.sampleRate, true);
view.setUint32(28, this.sampleRate * 4, true);
view.setUint16(32, 1 * 2, true);
view.setUint16(34, 16, true);
this.writeUTFBytes(view, 36, 'data');
view.setUint32(40, audioData.length * 2, true);
var length = audioData.length;
var index = 44;
var volume = 1;
for (var i = 0; i < length; i++) {
view.setInt16(index, audioData[i] * (0x7FFF * volume), true);
index += 2;
}
return buffer;
},
downloadFile(url) {
if ('download' in document.createElement('a')) {
var elink = document.createElement('a');
elink.style.display = 'none';
elink.download = 'demo.wav';
elink.href = url;
document.body.appendChild(elink);
elink.click();
window.URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
}
}
},
mounted() {}
})