#javascript #node.js #audio #ffmpeg #discord.js
#javascript #node.js #Аудио #ffmpeg #discord.js
Вопрос:
итак, это бот discord для записи голосового чатаhttps://hatebin.com/hgjlazacri Теперь бот работает отлично, но проблема в том, что звук звучит немного глубже и медленнее, чем обычно.. Почему это происходит? как я могу сделать звук 1: 1..
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 { getAudioDurationInSeconds } = require('get-audio-duration');
const cp = require('child_process');
const path1 = require('path');
const Enmap = require('enmap');
const UserRecords = require("./models/userrecords.js")
const ServerRecords = require("./models/serverrecords.js")
let prefix = `$`
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 = "";
const mongoose = require("mongoose");
const MongoClient = require('mongodb').MongoClient;
mongoose.connect('SECRRET',{
useNewUrlParser: true
}, function(err){
if(err){
console.log(err);
}else{
console.log("Database connection initiated");
}
});
require("dotenv").config()
function bufferToStream(buffer) {
let stream = new Readable();
stream.push(buffer);
return stream;
}
client.commands = new Enmap();
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 = `35.226.244.186`;
}
fqdn = `${protocol}://${host}:${port}`
app.listen(port, `0.0.0.0`, () => {
console.log(`Listening on port ${port} for ${host} at fqdn ${fqdn}`)
})
});
let randomArr = []
let finalArrWithIds = []
let variable = 0
client.on('message', async message => {
console.log(`fuck`);
if(message.content === `$record`){
mp3Paths = []
finalArrWithIds = []
let membersToScrape = Array.from(message.member.voice.channel.members.values());
membersToScrape.forEach((member) => {
if(member.id === `749250882830598235`) {
console.log(`botid`);
}
else {
finalArrWithIds.push(member.id)
}
})
const randomNumber = Math.floor(Math.random() * 100)
randomArr = []
randomArr.push(randomNumber)
}
const generateSilentData = async (silentStream, memberID) => {
console.log(`recordingnow`)
while(recording) {
if (!currently_recording[memberID]) {
silentStream.push(silence_buffer);
}
await new Promise(r => setTimeout(r, 20));
}
return "done";
}
console.log(generateSilentData, `status`)
function generateOutputFile(channelID, memberID) {
const dir = `./recordings/${channelID}/${memberID}`;
fs.ensureDirSync(dir);
const fileName = `${dir}/${randomArr[0]}.aac`;
console.log(`${fileName} ---------------------------`);
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, `outputfile here`);
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(44100)
.audioChannels(2)
.on('error', (error) => {console.log(error)})
.audioCodec('aac')
.format('adts')
.pipe(outputFile)
}
})
} else {
message.reply('You need to join a voice channel first!');
}
}
if (message.content === config.prefix config.stop_command) {
let date = new Date();
let dd = String(date.getDate()).padStart(2, '0');
let mm = String(date.getMonth() 1).padStart(2, '0');
let yyyy = date.getFullYear();
date = mm '/' dd '/' yyyy;
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]}` '.aac';
let mergedOutputFile = mergedOutputFolder file_name;
let download_path = message.member.voice.channel.id `/${randomArr[0]}/` file_name;
let mixedOutput = new ffmpeg();
console.log(mp3Paths, `mp3pathshere`);
mp3Paths.forEach((mp3Path) => {
mixedOutput.addInput(mp3Path);
})
console.log(mp3Paths);
//mixedOutput.complexFilter('amix=inputs=2:duration=longest');
mixedOutput.complexFilter('amix=inputs=' mp3Paths.length ':duration=longest');
let processEmbed = new Discord.MessageEmbed().setTitle(`Audio Processing.`)
processEmbed.addField(`Audio processing starting now..`, `Processing Audio`)
processEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/748610998985818202/speaker.png`)
processEmbed.setColor(` #00FFFF`)
const processEmbedMsg = await message.channel.send(processEmbed)
async function saveMp3(mixedData, outputMixed) {
console.log(`${mixedData} MIXED `)
return new Promise((resolve, reject) => {
mixedData.on('error', reject).on('progress',
async (progress) => {
let processEmbedEdit = new Discord.MessageEmbed().setTitle(`Audio Processing.`)
processEmbedEdit.addField(`Processing: ${progress.targetSize} KB converted`, `Processing Audio`)
processEmbedEdit.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/748610998985818202/speaker.png`)
processEmbedEdit.setColor(` #00FFFF`)
processEmbedMsg.edit(processEmbedEdit)
console.log('Processing: ' progress.targetSize ' KB converted');
}).on('end', () => {
console.log('Processing finished !');
resolve()
}).saveToFile(outputMixed);
console.log(`${outputMixed} IT IS HERE`);
})
}
// mixedOutput.saveToFile(mergedOutputFile);
await saveMp3(mixedOutput, mergedOutputFile);
console.log(`${mixedOutput} IN HEREEEEEEE`);
// 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}/${file_name}`
let errorThrown = false
try {
fs.copySync(sourceFile, destionationFile);
} catch (err) {
errorThrown = true
await message.channel.send(`Error: ${err.message}`)
}
const usersWithTag = finalArrWithIds.map(user => `n <@${user}>`);
let timeSpent = await getAudioDurationInSeconds(`public/${guildName}/${file_name}`)
let timesSpentRound = Math.floor(timeSpent)
let finalTimeSpent = timesSpentRound / 60
let finalTimeForReal = Math.floor(finalTimeSpent)
if(!errorThrown){
//--------------------- server recording save START
class GeneralRecords {
constructor(generalLink, date, voice, time) {
this.generalLink = generalLink;
this.date = date;
this.note = `no note`;
this.voice = voice;
this.time = time
}
}
let newGeneralRecordClassObject = new GeneralRecords(`${fqdn}/public/${guildName}/${file_name}`, date, usersWithTag, finalTimeForReal)
let checkingServerRecord = await ServerRecords.exists({userid: `server`})
if(checkingServerRecord === true){
existingServerRecord = await ServerRecords.findOne({userid: `server`})
existingServerRecord.content.push(newGeneralRecordClassObject)
await existingServerRecord.save()
}
if(checkingServerRecord === false){
let serverRecord = new ServerRecords()
serverRecord.userid = `server`
serverRecord.content.push(newGeneralRecordClassObject)
await serverRecord.save()
}
//--------------------- server recording save STOP
}
//--------------------- personal recording section START
for( member of finalArrWithIds) {
let personal_download_path = message.member.voice.channel.id `/${member}/` file_name;
let sourceFilePersonal = `${__dirname}/recordings/${personal_download_path}`
let destionationFilePersonal = `${__dirname}${serveExist}/${member}/${file_name}`
await fs.copySync(sourceFilePersonal, destionationFilePersonal);
const user = client.users.cache.get(member);
console.log(user, `user here`);
try {
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
ffmpeg(`public/${guildName}/${member}/${file_name}`)
.audioFilters('silenceremove=stop_periods=-1:stop_duration=1:stop_threshold=-90dB')
.output(`public/${guildName}/${member}/personal-${file_name}`)
.on(`end`, function () {
console.log(`DONE`);
})
.on(`error`, function (error) {
console.log(`An error occured` error.message)
})
.run();
}
catch (error) {
console.log(error)
}
// ----------------- SAVING PERSONAL RECORDING TO DATABASE START
class PersonalRecords {
constructor(generalLink, personalLink, date, time) {
this.generalLink = generalLink;
this.personalLink = personalLink;
this.date = date;
this.note = `no note`;
this.time = time;
}
}
let timeSpentPersonal = await getAudioDurationInSeconds(`public/${guildName}/${file_name}`)
let timesSpentRoundPersonal = Math.floor(timeSpentPersonal)
let finalTimeSpentPersonal = timesSpentRoundPersonal / 60
let finalTimeForRealPersonal = Math.floor(finalTimeSpentPersonal)
let newPersonalRecordClassObject = new PersonalRecords(`${fqdn}/public/${guildName}/${file_name}`, `${fqdn}/public/${guildName}/${member}/personal-${file_name}`, date, finalTimeForRealPersonal)
let checkingUserRecord = await UserRecords.exists({userid: member})
if(checkingUserRecord === true){
existingUserRecord = await UserRecords.findOne({userid: member})
existingUserRecord.content.push(newPersonalRecordClassObject)
await existingUserRecord.save()
}
if(checkingUserRecord === false){
let newRecord = new UserRecords()
newRecord.userid = member
newRecord.content.push(newPersonalRecordClassObject)
await newRecord.save()
}
// ----------------- SAVING PERSONAL RECORDING TO DATABASE END
const endPersonalEmbed = new Discord.MessageEmbed().setTitle(`Your performance was amazing ! Review it here :D`)
endPersonalEmbed.setColor('#9400D3')
endPersonalEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/745381641324724294/vinyl.png`)
endPersonalEmbed.addField(`📅 Date`, `${date}`)
endPersonalEmbed.addField(`⏰ Time spent by you`, `${finalTimeForRealPersonal} minute(s)`)
endPersonalEmbed.addField(`🔻 Download`, `${fqdn}/public/${guildName}/${member}/personal-${file_name}`)
endPersonalEmbed.setFooter(`Use `$myrecordings` to get a list of all your recordings !`)
user.send(endPersonalEmbed)
}
// ----------------- personal recording section OVER
const endEmbed = new Discord.MessageEmbed().setTitle(`I will be honest, that recording session was sick !`)
endEmbed.setColor('#9400D3')
endEmbed.addField(`🎙️ Voices`, usersWithTag)
endEmbed.addField(`⏰ Time spent`, `${finalTimeForReal} minute(s)`)
endEmbed.addField(`🔻 Download`, `${fqdn}/public/${guildName}/${file_name}`)
endEmbed.setThumbnail(`https://media.discordapp.net/attachments/730811581046325348/745370416352067704/microphone.png`)
endEmbed.setFooter(`Check your DMs for a recording of your personal voice during this session.`)
message.channel.send(endEmbed)
} else {
message.reply('You need to join a voice channel first!');
}
}
if (message.content.indexOf(prefix) !== 0) return;
const args = message.content.slice(prefix.length).trim().split(/ /g);
const command = args.shift().toLowerCase();
const cmd = client.commands.get(command);
if (!cmd) return;
cmd.run(client, message, args);
});
fs.readdir('./commands/', async (err, files) => {
if (err) return console.error;
files.forEach(file => {
if (!file.endsWith('.js')) return;
let props = require(`./commands/${file}`);
let cmdName = file.split('.')[0];
console.log(`Loaded command '${cmdName}'`);
// Register extra Listeners
client.commands.set(cmdName, props);
});
});
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()
Теперь бот работает отлично, но проблема в том, что звук звучит немного глубже и медленнее, чем обычно.. Почему это происходит? как я могу сделать звук 1: 1..
Как мне исправить несоответствие частоты дискретизации
Примечание; Я уже пытался установить частоту звука как 48000, но это не имело никакого значения.
Комментарии:
1. Что, если вы вообще отключите настройку частоты дискретизации звука?
2. сбросьте настройку частоты дискретизации? Я не совсем понимаю 😅
3. Удалить / delete эту строку полностью:
.audioFrequency(44100)
4. @Brad да, это дефо сделало его лучше, хотя я думаю, что голос все еще немного пьянее, чем обычно..