You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 12:23:02