如何在Electron应用中录制并编码生成MP3音频?
Electron录音转MP3:解决lamejs编码静音问题
问题背景
在Electron应用中实现录音并输出MP3功能,渲染进程通过MediaRecorder完成录音,将数据通过IPC传递至主进程后,使用lamejs编码仅生成静音文件。需要修改主进程代码,改为编码传入的录音ArrayBuffer。
核心问题分析
MediaRecorder默认录制的是WebM/OPUS格式的压缩音频,并非lamejs所需的原始PCM音频数据(Int16Array格式),直接传递Blob转成的ArrayBuffer无法被正确编码。- 当前主进程代码中
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
相关产品推荐
相关产品推荐

