使用 fluent-ffmpeg 与之前录制的音频重叠

Posted

技术标签:

【中文标题】使用 fluent-ffmpeg 与之前录制的音频重叠【英文标题】:Audio overlapping from previous recording with fluent-ffmpeg 【发布时间】:2020-12-04 06:44:37 【问题描述】:
const Discord = require('discord.js');
const client = new Discord.Client();

const ffmpegInstaller = require('@ffmpeg-installer/ffmpeg');
const ffmpeg = require('fluent-ffmpeg');

ffmpeg.setFfmpegPath(ffmpegInstaller.path);
const fs = require('fs-extra')
const mergeStream = require('merge-stream');
const config = require('./config.json');
const cp = require('child_process');
const path1 = require('path');



class Readable extends require('stream').Readable  _read()  

let recording = false;

let currently_recording = ;

let mp3Paths = [];

const silence_buffer = new Uint8Array(3840);

const express = require('express')
const app = express()
const port = 3000
const publicIP = require('public-ip')

const  program  = require('commander');
const  path  = require('@ffmpeg-installer/ffmpeg');
const version = '0.0.1'
program.version(version);

let debug = false
let runProd = false

let fqdn = "";

require("dotenv").config()


function bufferToStream(buffer) 
    let stream = new Readable();
    stream.push(buffer);
    return stream;








client.on('ready', async () => 
    console.log(`Logged in as $client.user.tag`);

    let host = "localhost"

    

    let ip = await publicIP.v4();

    let protocol = "http";
    if (!runProd) 
        host = "localhost"
     else 
        host = ip;
    
    fqdn = `$protocol://$host:$port`
    app.listen(port, host, () => 
        console.log(`Listening on port $port for $host at fqdn $fqdn`)
    )
);
let randomArr = []
let randomArr2 = []
let finalArrWithIds = []
let variable = 0
client.on('message', async message => 
    
    if(message.content === `$record`)
        finalArrWithIds = []
        let membersToScrape = Array.from(message.member.voice.channel.members.values());
        membersToScrape.forEach((member) => 
            if(member.id === `740372581118640149`) 
                console.log(`botid`);
            
            else 
                finalArrWithIds.push(member.id)
            
            
        )
        const randomNumber = Math.floor(Math.random() * 100)
        randomArr = []
        randomArr.push(randomNumber)
        const randomNumber2 = Math.floor(Math.random() * 100)
        randomArr2 = []
        randomArr.push(randomNumber2)
    
   console.log(finalArrWithIds)
    console.log(`HERE IT IS !!!!!!!!!!!! $randomArr[0]`)
    
    const generateSilentData = async (silentStream, memberID) => 
        while(recording) 
            if (!currently_recording[memberID]) 
                silentStream.push(silence_buffer);
            
            await new Promise(r => setTimeout(r, 20));
        
        return "done";
    
    
    function generateOutputFile(channelID, memberID) 
        const dir = `./recordings/$channelID/$memberID`;
        fs.ensureDirSync(dir);
        const fileName = `$dir/$randomArr[0].mp3`;
        return fs.createWriteStream(fileName);
    
    
    if (!fs.existsSync("public")) 
        fs.mkdirSync("public");
    
    app.use("/public", express.static("./public"));
  if (!message.guild) return;

  if (message.content === config.prefix + config.record_command) 
    if (recording) 
        message.reply("bot is already recording");
        return
    
    if (message.member.voice.channel) 
        recording = true;
        const connection = await message.member.voice.channel.join();
        const dispatcher = connection.play('./audio.mp3');

        connection.on('speaking', (user, speaking) => 
            if (speaking.has('SPEAKING')) 
                currently_recording[user.id] = true;
             else 
                currently_recording[user.id] = false;
            
        )


        let members = Array.from(message.member.voice.channel.members.values());
        members.forEach((member) => 

            if (member.id != client.user.id) 
                let memberStream = connection.receiver.createStream(member, mode : 'pcm', end : 'manual')

                let outputFile = generateOutputFile(message.member.voice.channel.id, member.id);
                console.log(outputFile);
                mp3Paths.push(outputFile.path);

                silence_stream = bufferToStream(new Uint8Array(0));
                generateSilentData(silence_stream, member.id).then(data => console.log(data));
                let combinedStream = mergeStream(silence_stream, memberStream);

                ffmpeg(combinedStream)
                    .inputFormat('s32le')
                    .audioFrequency(48000)
                    .audioChannels(2)
                    .on('error', (error) => console.log(error))
                    .audioCodec('libmp3lame')
                    .format('mp3')
                    // .audioFilters('silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-90dB')
                    .pipe(outputFile)
            
        )
     else 
      message.reply('You need to join a voice channel first!');
    
  

  if (message.content === config.prefix + config.stop_command) 
    let currentVoiceChannel = message.member.voice.channel;
    if (currentVoiceChannel) 
        recording = false;
        await currentVoiceChannel.leave();

        let mergedOutputFolder = './recordings/' + message.member.voice.channel.id + `/$randomArr[0]/`;
        fs.ensureDirSync(mergedOutputFolder);
        let file_name = `$randomArr[0]` + '.mp3';
        let mergedOutputFile = mergedOutputFolder + file_name;
    
    let download_path = message.member.voice.channel.id + `/$randomArr[0]/` + file_name;

        let mixedOutput = new ffmpeg();
        mp3Paths.forEach((mp3Path) => 
            mixedOutput.addInput(mp3Path);
        )
        //mixedOutput.complexFilter('amix=inputs=2:duration=longest');
        mixedOutput.complexFilter('amix=inputs=' + mp3Paths.length + ':duration=longest');
        function saveMp3(mixedData, outputMixed) 
            return new Promise((resolve, reject) => 
                mixedData.on('error', reject).on('progress',
                (progress) => 
                    console.log('Processing: ' + progress.targetSize + ' KB converted');
                ).on('end', () => 
                    console.log('Processing finished !');
                    resolve()
                ).saveToFile(outputMixed);
            )
        
        // mixedOutput.saveToFile(mergedOutputFile);
        await saveMp3(mixedOutput, mergedOutputFile);

        // We saved the recording, now copy the recording
        if (!fs.existsSync(`./public`)) 
            fs.mkdirSync(`./public`);
        
        let sourceFile = `$__dirname/recordings/$download_path`
        console.log(`DOWNLOAD PATH HERE $download_path`)
        const guildName = message.guild.id;
        const serveExist = `/public/$guildName`
        if (!fs.existsSync(`.$serveExist`)) 
            fs.mkdirSync(`.$serveExist`)
        
        let destionationFile = `$__dirname$serveExist/$member-$file_name`

        let errorThrown = false
        try 
            fs.copySync(sourceFile, destionationFile);
         catch (err) 
            errorThrown = true
            await message.channel.send(`Error: $err.message`)
        
        if (!errorThrown) 
            message.channel.send(`Link to full recording located at: $fqdn/public/$guildName/$member-$file_name`);
        
        

        

     else 
      message.reply('You need to join a voice channel first!');
    
   else 
    if (message.content.split(/\r\n|\r|\n/).length > config.line_length_limit && config.channel_name_log.includes(message.channel.name)) 
        file = `./recordings/$'text_logs'/$message.member.id/logs.txt`;
        fs.ensureFileSync(file);
        fs.appendFileSync(file, 'Channel: ' + message.channel.name + '\n' + message.createdAt + '\n' + message.content + '\n\n');

        let date = [`a`,`b`,`c`,`d`]
        let tmp_file_name = date[0] + date[1] + date[2];
        let daily_file = `./recordings/$'text_logs'/$tmp_file_name.txt`;
        fs.ensureFileSync(daily_file);
        fs.appendFileSync(daily_file, 'Channel: ' + message.channel.name + '\n' + message.createdAt + '\n' + message.content + '\n\n');
      
  
);

async function main() 
    program.option('-debug')
    program.option('-prod')

    program.parse(process.argv)

    console.log(program.opts())
    if (program.Debug != undefined) 
      debug = !debug
    
    if (program.Prod != undefined) 
      runProd = !runProd
    
    if (runProd) 
        client.login(process.env.DISCORD_TOKEN_PROD).catch(e => 
            console.log("ERROR")
            console.log(e)
        )
     else 
        client.login(process.env.DISCORD_TOKEN_TEST).catch(e => 
            console.log("ERROR")
            console.log(e)
        )
    

main()

所以我正在开发一个不和谐的机器人,它会在用户写入 !record 时在语音通道中记录用户的音频,并在他们写入 !stop 时停止录制。 现在,问题是如果你录制一次完全没问题,但是当你第二次录制时,randomArr[0] 文件夹中的文件也有之前录制的音频重叠,为什么会发生这种情况?如果我重新启动服务器,那么它工作正常,直到我记录第二次..

【问题讨论】:

【参考方案1】:

把它修好了。 mp3Paths 提出了问题, 必须在记录消息事件中清空它。 修复了它。

【讨论】:

以上是关于使用 fluent-ffmpeg 与之前录制的音频重叠的主要内容,如果未能解决你的问题,请参考以下文章

wavesurfer fluent-ffmpeg提取音频

fluent-ffmpeg 常用函数

在Javascript中将立体声音频转换为单声道

在Javascript中将立体声音频转换为单声道

iOS音频与视频的开发- 使用AVAudioRecorder进行录制音频

ios - 播放视频后录制音频