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

如何在Electron应用中录制并编码生成MP3音频?

Electron录音转MP3:解决lamejs编码静音问题

问题背景

在Electron应用中实现录音并输出MP3功能,渲染进程通过MediaRecorder完成录音,将数据通过IPC传递至主进程后,使用lamejs编码仅生成静音文件。需要修改主进程代码,改为编码传入的录音ArrayBuffer。

核心问题分析

  1. MediaRecorder默认录制的是WebM/OPUS格式的压缩音频,并非lamejs所需的原始PCM音频数据(Int16Array格式),直接传递Blob转成的ArrayBuffer无法被正确编码。
  2. 当前主进程代码中new Int16Array(44100)生成的是静音数据,这是输出文件无声音的直接原因。

修正方案

1. 渲染进程:将录制音频转为PCM原始数据

修改app.tsx中的onstop回调,通过AudioContext解码录制的音频Blob,转换为lamejs支持的Int16Array格式PCM数据:

let mediaRecorder: MediaRecorder | null = null
let recordedChunks: Blob[] = []

window.api.startVoiceRecording(() => {
  navigator.mediaDevices
    .getUserMedia({ audio: true })
    .then((stream) => {
      mediaRecorder = new MediaRecorder(stream)

      mediaRecorder.addEventListener('dataavailable', (event) => {
        recordedChunks.push(event.data)
      })

      mediaRecorder.onstop = async (e) => {
        const audioBlob = new Blob(recordedChunks, { type: mediaRecorder.mimeType })
        const audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
        
        // 解码音频Blob为AudioBuffer(包含原始PCM数据)
        const arrayBuffer = await audioBlob.arrayBuffer()
        const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
        
        // 提取单声道数据(若为立体声需处理双声道)
        const channelData = audioBuffer.getChannelData(0)
        // 将Float32格式PCM(范围-1~1)转换为Int16格式(范围-32768~32767)
        const pcmData = new Int16Array(channelData.length)
        for (let i = 0; i < channelData.length; i++) {
          pcmData[i] = Math.max(-32768, Math.min(32767, Math.round(channelData[i] * 32767)))
        }

        // 传递PCM数据的ArrayBuffer到主进程
        window.api.onNewMp3Blob(pcmData.buffer)

        recordedChunks = []
        mediaRecorder = null
        audioContext.close()
      }

      mediaRecorder.start()
    })
    .catch((err) => {
      console.error('麦克风访问失败:', err)
    })
})

window.api.stopVoiceRecording(() => {
  if (mediaRecorder) {
    mediaRecorder.stop()
    console.log('录音停止,分片数量:', recordedChunks.length)
  }
})

2. 主进程:编码传入的PCM数据

修改voice.ts,接收渲染进程传递的ArrayBuffer,转换为Int16Array后用lamejs编码:

import { ipcMain } from 'electron'
import lamejs from 'lamejs'
import fs from 'fs'
import path from 'path'
import os from 'os'

ipcMain.on('c-onNewMp3Blob', (event, args: ArrayBuffer) => {
  // 将传入的ArrayBuffer转为Int16Array格式的PCM数据
  const pcmData = new Int16Array(args)
  // 初始化编码器:参数依次为声道数、采样率、比特率
  const mp3encoder = new lamejs.Mp3Encoder(1, 44100, 128)
  const mp3Data: Buffer[] = []

  // lamejs每次最多处理1152帧数据,需分片编码
  const chunkSize = 1152
  for (let i = 0; i < pcmData.length; i += chunkSize) {
    const chunk = pcmData.subarray(i, i + chunkSize)
    const mp3Chunk = mp3encoder.encodeBuffer(chunk)
    if (mp3Chunk.length > 0) {
      mp3Data.push(Buffer.from(mp3Chunk))
    }
  }

  // 刷新编码器,获取剩余的MP3数据
  const finalChunk = mp3encoder.flush()
  if (finalChunk.length > 0) {
    mp3Data.push(Buffer.from(finalChunk))
  }

  // 合并MP3数据并写入文件
  const mp3Buffer = Buffer.concat(mp3Data)
  fs.writeFile(path.join(os.homedir(), 'result.mp3'), mp3Buffer, (err) => {
    if (err) console.error('写入文件失败:', err)
    else console.log('MP3文件已成功保存')
  })
})

关键注意事项

  • 采样率匹配:确保lamejs初始化的采样率与audioBuffer.sampleRate一致(可从audioBuffer.sampleRate获取实际采样率)。
  • 声道处理:若录制的是立体声,需将Mp3Encoder第一个参数设为2,并分别传入左右声道的Int16Array(使用audioBuffer.getChannelData(0)和getChannelData(1))。
  • 数据分片:lamejs的encodeBuffer方法单次处理上限为1152帧,大数据量时必须分片处理,否则会导致编码错误。

内容的提问来源于stack exchange,提问作者Johnson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 23:42:08