声卡功能完善

This commit is contained in:
1 2025-06-24 09:04:13 +08:00
parent 8c6c7e5f09
commit d1da4a85bc
5 changed files with 72 additions and 42 deletions

BIN
dist.zip

Binary file not shown.

View File

@ -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",

View File

@ -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 || [];

View File

@ -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);

View File

@ -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);
// Float32Int16
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;
clearInterval(this.timer);
//
this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
if (!this.isRecording) return;
clearInterval(this.timer);
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;
}
}
}