nodejs 使用speaker + ffmpeg 实现静默播放MP3
nodejs播放MP3
依赖:
npm i speaker
下载 ffmpeg.exe 文件放到可执行目录
const ffmpeg = require('fluent-ffmpeg');
const Speaker = require('speaker');
const WaveFile = require('wavefile').WaveFile;
const tempDir = require('os').tmpdir();
const fs = require('fs');
const { spawn } = require('child_process');function convertMP3ToWAV(inputPath, outputPath) {return new Promise((resolve, reject) => {ffmpeg(inputPath).toFormat('wav').audioCodec('pcm_s16le') // 16位有符号整数.audioChannels(1) // 单声道(适合语音).audioFrequency(16000) // 16kHz 采样率(与 edge-tts 一致).on('end', () => {console.log(`✅ 转换完成: ${outputPath}`);resolve(outputPath);}).on('error', (err) => {console.error('❌ 转换失败:', err);reject(err);}).save(outputPath);});
}async function play() {const mp3Path = './output.mp3'//`${tempDir}/speech_${Date.now()}.mp3`;const wavPath = mp3Path.replace('.mp3', '.wav');// if (!fs.existsSync(wavPath)) {// 2. 转换为 WAV// await convertMP3ToWAV(mp3Path, wavPath);// console.log('✅ WAV 已生成:', wavPath);// // }// // 3. 用 speaker 播放// await playWAVSilent(wavPath);playMP3WithFFmpeg(mp3Path)
}async function playWAVSilent(filePath) {return new Promise((resolve, reject) => {const buffer = fs.readFileSync(filePath);// 读取 WAV 文件头获取参数const waveFile = new WaveFile(buffer);const format = waveFile.fmt;const speaker = new Speaker({channels: format.numChannels,bitDepth: format.bitDepth,sampleRate: format.sampleRate,});speaker.write(buffer);speaker.end();speaker.on('close', () => {console.log('✅ 静默播放完成');resolve();});speaker.on('error', (err) => {console.error('❌ 播放错误:', err);reject(err);});});
}function playMP3WithFFmpeg(inputPath) {return new Promise((resolve, reject) => {// 获取 ffmpeg 路径(假设放在项目根目录)const ffmpegPath = './ffmpeg.exe'; // 👈 修改为你自己的路径// 构造 ffmpeg 命令:解码 MP3 → 输出为 PCM(stdout)const args = ['-i', inputPath, // 输入文件'-f', 's16le', // 输出格式:16位有符号小端 PCM'-ac', '1', // 单声道'-ar', '16000', // 采样率 16kHz'-acodec', 'pcm_s16le', // 编码器'pipe:1' // 输出到 stdout];// 启动 ffmpeg 子进程const ffmpegProc = spawn(ffmpegPath, args);// 创建 Speaker 实例let speaker = null;// 监听 ffmpeg 输出ffmpegProc.stdout.on('data', (chunk) => {if (!speaker) {// 第一次收到数据时初始化 Speakerspeaker = new Speaker({channels: 1,bitDepth: 16,sampleRate: 16000,});// 将 PCM 数据写入扬声器ffmpegProc.stdout.pipe(speaker);// 播放完成回调speaker.on('close', () => {console.log('✅ 静默播放完成');resolve();});speaker.on('error', (err) => {console.error('❌ 播放错误:', err);reject(err);});}});// 监听错误ffmpegProc.stderr.on('data', (data) => {// console.error('FFmpeg 错误:', data.toString());});ffmpegProc.on('error', (err) => {console.error('❌ ffmpeg 启动失败:', err);reject(err);});ffmpegProc.on('close', (code) => {if (code !== 0) {reject(new Error(`ffmpeg 退出码: ${code}`));}});});
}play()
