如何在JavaScript中区分系统音频与麦克风音频(Electron+React场景)
分别录制麦克风与系统音频的实现方案
在Electron+React环境中,要分开获取麦克风和系统音频,核心是通过Electron的desktopCapturer获取系统音频源,再结合浏览器原生的getUserMedia获取麦克风流,最后用MediaRecorder分别录制两个流并保存到对应变量。
步骤1:配置Electron主进程与预加载脚本
首先需要在主进程中启用必要权限,并通过预加载脚本暴露系统音频获取方法(因为渲染进程无法直接访问Electron原生API)。
主进程代码(main.js)
const { app, BrowserWindow } = require('electron'); const path = require('path'); function createWindow() { const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false } }); // 加载React开发服务器地址或打包后的html文件 mainWindow.loadURL('http://localhost:3000'); } app.whenReady().then(createWindow); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
预加载脚本(preload.js)
用于向渲染进程暴露获取系统音频源的方法:
const { contextBridge, desktopCapturer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { getSystemAudioSource: async () => { const sources = await desktopCapturer.getSources({ types: ['audio'], audio: true }); // 根据系统语言匹配音频源名称,中文系统用"系统音频",英文用"System Audio" return sources.find(source => source.name.includes('系统') || source.name.includes('System') ); } });
步骤2:React组件中实现分别录制
在React组件中,分别初始化麦克风流和系统音频流,用MediaRecorder单独录制,最终将结果保存到microphoneAudio和systemAudio变量。
import { useState } from 'react'; function AudioRecorder() { const [micRecorder, setMicRecorder] = useState(null); const [systemRecorder, setSystemRecorder] = useState(null); const [microphoneAudio, setMicrophoneAudio] = useState(null); const [systemAudio, setSystemAudio] = useState(null); // 初始化麦克风录制 const initMicRecording = async () => { try { const micStream = await navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true } }); const recorder = new MediaRecorder(micStream); const chunks = []; recorder.ondataavailable = (e) => chunks.push(e.data); recorder.onstop = () => { const audioBlob = new Blob(chunks, { type: 'audio/webm' }); setMicrophoneAudio(audioBlob); // 停止流释放资源 micStream.getTracks().forEach(track => track.stop()); }; setMicRecorder(recorder); } catch (err) { console.error('麦克风权限获取失败:', err); } }; // 初始化系统音频录制 const initSystemRecording = async () => { try { const systemSource = await window.electronAPI.getSystemAudioSource(); if (!systemSource) { console.error('未找到系统音频源'); return; } const systemStream = await navigator.mediaDevices.getUserMedia({ audio: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: systemSource.id } } }); const recorder = new MediaRecorder(systemStream); const chunks = []; recorder.ondataavailable = (e) => chunks.push(e.data); recorder.onstop = () => { const audioBlob = new Blob(chunks, { type: 'audio/webm' }); setSystemAudio(audioBlob); systemStream.getTracks().forEach(track => track.stop()); }; setSystemRecorder(recorder); } catch (err) { console.error('系统音频源获取失败:', err); } }; // 开始录制 const startRecording = () => { initMicRecording(); initSystemRecording(); // 延迟确保Recorder初始化完成 setTimeout(() => { micRecorder?.start(); systemRecorder?.start(); }, 500); }; // 停止录制 const stopRecording = () => { micRecorder?.stop(); systemRecorder?.stop(); }; // 发送到后端示例 const sendToBackend = async () => { if (!microphoneAudio || !systemAudio) return; const formData = new FormData(); formData.append('micAudio', microphoneAudio, 'mic-recording.webm'); formData.append('systemAudio', systemAudio, 'system-recording.webm'); try { await fetch('/api/upload-audio', { method: 'POST', body: formData }); console.log('音频上传成功'); } catch (err) { console.error('音频上传失败:', err); } }; return ( <div> <button onClick={startRecording}>开始录制</button> <button onClick={stopRecording}>停止录制</button> <button onClick={sendToBackend}>发送到后端</button> {microphoneAudio && <p>麦克风音频已录制完成</p>} {systemAudio && <p>系统音频已录制完成</p>} </div> ); } export default AudioRecorder;
注意事项
- 系统音频源的名称会随系统语言变化,需根据实际环境调整
preload.js中的匹配逻辑; - 录制格式默认是
webm,若需要其他格式(如mp3),可在MediaRecorder构造时指定对应MIME类型,或后续进行转码; - 需确保用户授予麦克风和桌面录制权限,否则会触发错误,建议添加权限拒绝的友好提示。
内容的提问来源于stack exchange,提问作者Arrow
相关产品推荐
相关产品推荐

