声卡功能完善
This commit is contained in:
parent
8c6c7e5f09
commit
d1da4a85bc
@ -62,6 +62,7 @@
|
|||||||
"json-loader": "^0.5.7",
|
"json-loader": "^0.5.7",
|
||||||
"jsonlint": "^1.6.3",
|
"jsonlint": "^1.6.3",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
|
"lamejs": "^1.2.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"minimatch": "^3.1.2",
|
"minimatch": "^3.1.2",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<script src="/js/jessibuca-pro/jessibuca-pro.js"></script>
|
<script src="/js/jessibuca-pro/jessibuca-pro.js"></script>
|
||||||
<script type="text/javascript" src="/js/ZLMRTCClient.js"></script>
|
<script type="text/javascript" src="/js/ZLMRTCClient.js"></script>
|
||||||
<script type="text/javascript" src="/js/EasyWasmPlayer.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>
|
<script>
|
||||||
var _hmt = _hmt || [];
|
var _hmt = _hmt || [];
|
||||||
|
@ -45,6 +45,8 @@ import 'vue-video-player/src/custom-theme.css';
|
|||||||
import 'video.js/dist/video-js.css';
|
import 'video.js/dist/video-js.css';
|
||||||
import BaiduMap from 'vue-baidu-map'; // 百度地图
|
import BaiduMap from 'vue-baidu-map'; // 百度地图
|
||||||
import '@/assets/styles/common.scss';
|
import '@/assets/styles/common.scss';
|
||||||
|
// import { Mp3Encoder } from 'lamejs';
|
||||||
|
|
||||||
|
|
||||||
import Contextmenu from 'vue-contextmenujs';
|
import Contextmenu from 'vue-contextmenujs';
|
||||||
Vue.use(Contextmenu);
|
Vue.use(Contextmenu);
|
||||||
|
@ -597,6 +597,12 @@ export default {
|
|||||||
},
|
},
|
||||||
isEditPlaylist: false,
|
isEditPlaylist: false,
|
||||||
editingPlaylistIndex: null,
|
editingPlaylistIndex: null,
|
||||||
|
_mp3Encoder: null,
|
||||||
|
_audioChunks: [],
|
||||||
|
_audioContext: null,
|
||||||
|
_sourceNode: null,
|
||||||
|
_processorNode: null,
|
||||||
|
_stream: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
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() {
|
async startRecording() {
|
||||||
if (this.isRecording) return;
|
if (this.isRecording) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
this.mediaRecorder = new MediaRecorder(stream);
|
audio: {
|
||||||
this.audioChunks = [];
|
sampleRate: 44100,
|
||||||
|
channelCount: 2,
|
||||||
this.mediaRecorder.ondataavailable = (event) => {
|
sampleSize: 16,
|
||||||
this.audioChunks.push(event.data);
|
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._sourceNode.connect(this._processorNode);
|
||||||
this.mediaRecorder.onstop = () => {
|
this._processorNode.connect(this._audioContext.destination);
|
||||||
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.isRecording = true;
|
this.isRecording = true;
|
||||||
this.recordingStatus = '正在录音...';
|
this.recordingStatus = '正在录音...';
|
||||||
this.recordingTime = 0;
|
this.recordingTime = 0;
|
||||||
|
this.timer = setInterval(() => this.recordingTime++, 1000);
|
||||||
// 开始计时
|
|
||||||
this.timer = setInterval(() => {
|
|
||||||
this.recordingTime++;
|
|
||||||
}, 1000);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.$message.error('无法访问麦克风');
|
console.error('录音失败:', error);
|
||||||
console.error('录音错误:', error);
|
this.$message.error(`录音失败: ${error.message}`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stopRecording() {
|
stopRecording() {
|
||||||
if (this.mediaRecorder && this.isRecording) {
|
if (!this.isRecording) return;
|
||||||
this.mediaRecorder.stop();
|
clearInterval(this.timer);
|
||||||
this.isRecording = false;
|
if (this._sourceNode) this._sourceNode.disconnect();
|
||||||
clearInterval(this.timer);
|
if (this._processorNode) this._processorNode.disconnect();
|
||||||
|
if (this._stream) this._stream.getTracks().forEach(track => track.stop());
|
||||||
// 停止所有音轨
|
// 刷新编码器缓冲区
|
||||||
this.mediaRecorder.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) {
|
formatTime(seconds) {
|
||||||
@ -2135,22 +2165,18 @@ export default {
|
|||||||
// 表格内部的滚动条样式
|
// 表格内部的滚动条样式
|
||||||
.el-table__body-wrapper {
|
.el-table__body-wrapper {
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 28px;
|
||||||
height: 12px;
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background: #f1f1f1;
|
background: #f5f5f5;
|
||||||
border-radius: 6px;
|
border-radius: 29px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: #c1c1c1;
|
background: #bfbfbf;
|
||||||
border-radius: 6px;
|
border-radius: 29px;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #a8a8a8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user