声卡功能完善
This commit is contained in:
parent
8c6c7e5f09
commit
d1da4a85bc
@ -62,6 +62,7 @@
|
||||
"json-loader": "^0.5.7",
|
||||
"jsonlint": "^1.6.3",
|
||||
"jszip": "^3.10.1",
|
||||
"lamejs": "^1.2.1",
|
||||
"lodash": "^4.17.21",
|
||||
"minimatch": "^3.1.2",
|
||||
"moment": "^2.29.4",
|
||||
|
@ -11,6 +11,7 @@
|
||||
<script src="/js/jessibuca-pro/jessibuca-pro.js"></script>
|
||||
<script type="text/javascript" src="/js/ZLMRTCClient.js"></script>
|
||||
<script type="text/javascript" src="/js/EasyWasmPlayer.js"></script>
|
||||
<script src="https://unpkg.com/lamejs@1.2.0/lame.min.js"></script>
|
||||
<!-- 百度统计,不需要可以删除 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
|
@ -45,6 +45,8 @@ import 'vue-video-player/src/custom-theme.css';
|
||||
import 'video.js/dist/video-js.css';
|
||||
import BaiduMap from 'vue-baidu-map'; // 百度地图
|
||||
import '@/assets/styles/common.scss';
|
||||
// import { Mp3Encoder } from 'lamejs';
|
||||
|
||||
|
||||
import Contextmenu from 'vue-contextmenujs';
|
||||
Vue.use(Contextmenu);
|
||||
|
@ -597,6 +597,12 @@ export default {
|
||||
},
|
||||
isEditPlaylist: false,
|
||||
editingPlaylistIndex: null,
|
||||
_mp3Encoder: null,
|
||||
_audioChunks: [],
|
||||
_audioContext: null,
|
||||
_sourceNode: null,
|
||||
_processorNode: null,
|
||||
_stream: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
@ -1165,51 +1171,75 @@ export default {
|
||||
},
|
||||
|
||||
// 录音相关方法
|
||||
floatTo16BitPCM(input) {
|
||||
const output = new Int16Array(input.length);
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
let s = Math.max(-1, Math.min(1, input[i]));
|
||||
output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
||||
}
|
||||
return output;
|
||||
},
|
||||
async startRecording() {
|
||||
if (this.isRecording) return;
|
||||
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
this.mediaRecorder = new MediaRecorder(stream);
|
||||
this.audioChunks = [];
|
||||
|
||||
this.mediaRecorder.ondataavailable = (event) => {
|
||||
this.audioChunks.push(event.data);
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
sampleRate: 44100,
|
||||
channelCount: 2,
|
||||
sampleSize: 16,
|
||||
autoGainControl: false,
|
||||
noiseSuppression: false,
|
||||
echoCancellation: false
|
||||
}
|
||||
});
|
||||
const Mp3Encoder = window.lamejs.Mp3Encoder;
|
||||
this._mp3Encoder = new Mp3Encoder(2, 44100, 128);
|
||||
this._audioChunks = [];
|
||||
this._audioContext = new AudioContext({ sampleRate: 44100 });
|
||||
this._sourceNode = this._audioContext.createMediaStreamSource(stream);
|
||||
this._processorNode = this._audioContext.createScriptProcessor(4096, 2, 2);
|
||||
this._stream = stream;
|
||||
this._processorNode.onaudioprocess = (event) => {
|
||||
const leftChannel = event.inputBuffer.getChannelData(0);
|
||||
const rightChannel = event.inputBuffer.getChannelData(1);
|
||||
// 修正:Float32转Int16
|
||||
const left = this.floatTo16BitPCM(leftChannel);
|
||||
const right = this.floatTo16BitPCM(rightChannel);
|
||||
const mp3Buffer = this._mp3Encoder.encodeBuffer(left, right);
|
||||
if (mp3Buffer.length > 0) {
|
||||
this._audioChunks.push(mp3Buffer);
|
||||
}
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = () => {
|
||||
const audioBlob = new Blob(this.audioChunks, { type: 'audio/mp3' });
|
||||
this.hasRecording = true;
|
||||
this.recordingStatus = '录音完成';
|
||||
// 创建音频预览URL
|
||||
this.audioUrl = URL.createObjectURL(audioBlob);
|
||||
};
|
||||
|
||||
// 设置数据收集间隔为100ms
|
||||
this.mediaRecorder.start(100);
|
||||
this._sourceNode.connect(this._processorNode);
|
||||
this._processorNode.connect(this._audioContext.destination);
|
||||
this.isRecording = true;
|
||||
this.recordingStatus = '正在录音...';
|
||||
this.recordingTime = 0;
|
||||
|
||||
// 开始计时
|
||||
this.timer = setInterval(() => {
|
||||
this.recordingTime++;
|
||||
}, 1000);
|
||||
this.timer = setInterval(() => this.recordingTime++, 1000);
|
||||
} catch (error) {
|
||||
this.$message.error('无法访问麦克风');
|
||||
console.error('录音错误:', error);
|
||||
console.error('录音失败:', error);
|
||||
this.$message.error(`录音失败: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
stopRecording() {
|
||||
if (this.mediaRecorder && this.isRecording) {
|
||||
this.mediaRecorder.stop();
|
||||
this.isRecording = false;
|
||||
if (!this.isRecording) return;
|
||||
clearInterval(this.timer);
|
||||
|
||||
// 停止所有音轨
|
||||
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
|
||||
if (this._sourceNode) this._sourceNode.disconnect();
|
||||
if (this._processorNode) this._processorNode.disconnect();
|
||||
if (this._stream) this._stream.getTracks().forEach(track => track.stop());
|
||||
// 刷新编码器缓冲区
|
||||
if (this._mp3Encoder) {
|
||||
const lastChunk = this._mp3Encoder.flush();
|
||||
if (lastChunk.length > 0) this._audioChunks.push(lastChunk);
|
||||
}
|
||||
// 生成 MP3 文件
|
||||
const mp3Blob = new Blob(this._audioChunks, { type: 'audio/mp3' });
|
||||
this.audioUrl = URL.createObjectURL(mp3Blob);
|
||||
this.hasRecording = true;
|
||||
this.recordingStatus = '录音完成';
|
||||
this.audioChunks = this._audioChunks;
|
||||
this.isRecording = false;
|
||||
},
|
||||
|
||||
formatTime(seconds) {
|
||||
@ -2135,22 +2165,18 @@ export default {
|
||||
// 表格内部的滚动条样式
|
||||
.el-table__body-wrapper {
|
||||
&::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 6px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 29px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
background: #bfbfbf;
|
||||
border-radius: 29px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user