How can I record voice from a VC using discord.js?

I want to record voice from a specific voice channel, I already tried to do it many times but didn't find the solution, I either get a slowed down .pcm audio, an unaudible audio, an audio that progressively slowes down or a 1 second long static sound audio. I'm on Node.js v20.12.2, also I'm coding in TypeScript
132 Replies
d.js toolkit
d.js toolkitβ€’7mo ago
- What's your exact discord.js npm list discord.js and node node -v version? - Not a discord.js issue? Check out #other-js-ts. - Consider reading #how-to-get-help to improve your question! - Explain what exactly your issue is. - Post the full error stack trace, not just the top part! - Show your code! - Issue solved? Press the button!
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
I already looked at it and implemented the code to my bot and it just gave me a bunch of .ogg files of short static sounds, i even cloned the "recorder" folder in that repository and still got the same issue
pat
patβ€’7mo ago
what's your original code for trying to get it in pcm & fwiw recorder worksonmymachine make sure you have encryption libraries for decrypting the data i guess? & opus libraries for decoding opus
const { generateDependencyReport } = require('@discordjs/voice');

console.log(generateDependencyReport());
const { generateDependencyReport } = require('@discordjs/voice');

console.log(generateDependencyReport());
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
this was my very original code trying to make the recording happen through a slash command: https://sourceb.in/ySomvmqOES similar to the voice-example in the discordjs github repository but without creating a different function
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
in that code the "pipeline" function popped up a type error tho (the link to that error is in the code of the link ive provided you), but i ignored it and still ran the code, and it just created a bunch of pcm files and when i listened to them raw on audacity they were just very short static sounds
No description
pat
patβ€’7mo ago
how about deps then
pat
patβ€’7mo ago
this
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
also didn't understand what you tried to say here oh yeah im installing them i just have sodium left
pat
patβ€’7mo ago
& for what its worth recorder works on my machine you dont need them all - only one
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
then i have all of them installed except sodium like it works for you?
pat
patβ€’7mo ago
yes and i get proper files that i can open and hear my voice in no static
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
but what code have you used? like your own or the one from the voice-examples repo
pat
patβ€’7mo ago
exactly the one from voice-examples/recorder
pat
patβ€’7mo ago
works with no changes
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
with the dependecies installed right
pat
patβ€’7mo ago
i'd hope they were installed otherwise i'd be decrypting packets from thin air
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
😭 so its okay if i don't have sodium installed?
pat
patβ€’7mo ago
yes sodiums a pain in the ass to install anyways
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
fr i see that it told me to change thee version of visual studio and it didn't let me i still have the same problem, even if im using the same code as in the repository :monkaStop: i installed the same dependecies in that repo code it creates a bunch of ogg or pcm files (yes i tried both extensions) and all of them are either static or just some weird sound
pat
patβ€’7mo ago
are you able to send one here out of curiosity
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
one of the audio files?
pat
patβ€’7mo ago
yes
pat
patβ€’7mo ago
and if you play that second one it works for you? what os are you using
pat
patβ€’7mo ago
and what are you using to open the file
pat
patβ€’7mo ago
i think the issue is you are missing a codec to properly play the audio
pat
patβ€’7mo ago
like to play the audio on your machine you need a codec to properly understand the data in the files
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
may i ask, what is a codec? 😭
pat
patβ€’7mo ago
tldr; translates the audio into something you can listen to
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
oohh i see so i should convert it to mp3? it would be audible
pat
patβ€’7mo ago
the ogg file you sent
pat
patβ€’7mo ago
looks like this in audacity for me
No description
pat
patβ€’7mo ago
is that right?
pat
patβ€’7mo ago
lol
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
maybe because i imported as raw data
pat
patβ€’7mo ago
if you just drag it in is it correct that's what i did
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
oh yeah i opened as a normal audio file and it looked like it looked to you
pat
patβ€’7mo ago
and it plays correctly?
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
yeah plays like on discord
pat
patβ€’7mo ago
okay so its not a problem with a codec it's that you are trying to read it incorrectly by opening as raw lol
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
yeah πŸ’€ and i guess for the file mass creation i should just place the createWriteStream outside of the speaking.on event listener
pat
patβ€’7mo ago
what oh you want longer audio files
pat
patβ€’7mo ago
increase the duration in end
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
that was just a portion of what i said in the voice call
pat
patβ€’7mo ago
when u stop speaking it will wait 100 ms before ending the audio stream
pat
patβ€’7mo ago
if u increase it to like 2000 u have 2 seconds before it cuts off
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
yeah i see is it bad if i set the end behavior to manual?
pat
patβ€’7mo ago
no idea what it entails honestly
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
because i wanted to do this feature for moderation and i prefer recording the whole voice call until its stopped manually
pat
patβ€’7mo ago
theoretically the opus stream should never end if you do that
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
yeah i have to call the .destroy function i suppose
pat
patβ€’7mo ago
yea just chuck a opusStream.destroy() when ur done i guess
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
for this error i should just place "as any" on the needed parameters bc i really dont know what is its problem
No description
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
thats my bot's source code not the voice-examples one
pat
patβ€’7mo ago
if you compile does it still show that error
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
no its just a type error from typescript
pat
patβ€’7mo ago
whats your typescript version
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
the latest one wait 5.5.4 is that even the latest
pat
patβ€’7mo ago
weird, i have 5.7.0-dev and 4.9.5 and it doesn't show that
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
you have two versions? i will try installing one of them maybe its something they have fixed on the dev version
pat
patβ€’7mo ago
well kinda, vscode version and globally installed version can just change it with ctrl shift p
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
oh okay uh also one question are you using the latest versions of @discordjs/voice and @discordjs/opus? or the same ones in the repository
pat
patβ€’7mo ago
if by latest you mean dev version yes
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
i meant this πŸ€“
No description
pat
patβ€’7mo ago
types shouldnt be different either way
pat
patβ€’7mo ago
Β―\_(ツ)_/Β―
pat
patβ€’7mo ago
No description
pat
patβ€’7mo ago
tired of type errors
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
pat is there any way you could send me here an example of your recordings? to see what it would sound like dont use your voice if u want just do fart sounds with yo mouth
pat
patβ€’7mo ago
πŸ’€
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
thats what i did i wanna compare both audios it can be short if you don't wanna do it just let me know tho its fine nevermind.
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
it now records, but im getting a warning of max listeners and it suddenly stops the recording: https://sourceb.in/Aat4yDAnQv
SourceBin
memory leak
Instantly share your code with the world.
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
it happens only when i talk
pat
patβ€’7mo ago
if (receiver.subscriptions.has(userId)) return;
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
oh it worked, thank you uh also, when i stop the recording by myself, i get a premature close error from the pipeline function is there any way to prevent that?
pat
patβ€’7mo ago
does it look like this
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^

Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close
at new NodeError (node:internal/errors:393:5)
at AudioReceiveStream.onclose (node:internal/streams/end-of-stream:142:30)
at AudioReceiveStream.emit (node:events:525:35)
at emitCloseNT (node:internal/streams/destroy:132:10)
at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
code: 'ERR_STREAM_PREMATURE_CLOSE'
}
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^

Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close
at new NodeError (node:internal/errors:393:5)
at AudioReceiveStream.onclose (node:internal/streams/end-of-stream:142:30)
at AudioReceiveStream.emit (node:events:525:35)
at emitCloseNT (node:internal/streams/destroy:132:10)
at process.processTicksAndRejections (node:internal/process/task_queues:81:21) {
code: 'ERR_STREAM_PREMATURE_CLOSE'
}
pat
patβ€’7mo ago
when are you ending it
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
when i run the "stop" subcommand wait
pat
patβ€’7mo ago
are you piping it into the file stream in that stop command or still in the start speaking part
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
in the start speaking part you want the whole command code so you can see?
pat
patβ€’7mo ago
naw just try the former piping it after you are done with the stream instead
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
i have the "pipeline" function in the "start" subcommand part and i destroy the opus, out streams and the voice connection aswell at the "stop" subcommand part when im done with the ogg stream?
pat
patβ€’7mo ago
pipe after you destroy the voice stream sry the file stream part was wrong i meant the ogg stream audio > ogg > file
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
wait so after running "VoiceConnection.destroy()" i should pipe the file?
pat
patβ€’7mo ago
the audioreceivestream opusstream
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
so i should place the
pipeline(audio_stream, ogg_stream , output, (err: NodeJS.ErrnoException | null) => {
if (err) console.error(`${Fg.makeRed("[STREAM_PIPELINE_ERROR]")} error recording file ${makeItalic(file_name_ex)} -`, err);
else console.info(`${Fg.makeGreen("[STREAM_PIPELINE_SUCCESS]")} recorded ${makeItalic(file_name_ex)}`);
});
pipeline(audio_stream, ogg_stream , output, (err: NodeJS.ErrnoException | null) => {
if (err) console.error(`${Fg.makeRed("[STREAM_PIPELINE_ERROR]")} error recording file ${makeItalic(file_name_ex)} -`, err);
else console.info(`${Fg.makeGreen("[STREAM_PIPELINE_SUCCESS]")} recorded ${makeItalic(file_name_ex)}`);
});
after the connection.destroy() function audio_stream is the opus stream output is the write stream
pat
patβ€’7mo ago
try it, should work but i am doing this from reading code not actual experience
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
alrighty nah it still gave me the same error, but its fine thank you for trying i will look for a solution if i find one
pat
patβ€’7mo ago
@eman this looks awful but
const receiver = connection.receiver;

for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
const receiver = connection.receiver;

for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
pat
patβ€’7mo ago
however, some weird side effects; no silence is recorded & it wont console log so if you speak, wait 5 seconds and then speak again it will be as if you didnt leave any gap at all
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
i don't understand what are you trying to tell me?
pat
patβ€’7mo ago
that code works & doesn't get premature end error to save w/ manual end behavior
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
using it with a for loop?
pat
patβ€’7mo ago
for loop doesn't matter; that would just give you all active subscriptions the stream.push(null) is the difference here
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
aahh "v" is the opus stream?
pat
patβ€’7mo ago
yes
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
but what end behavior manual?
pat
patβ€’7mo ago
yes
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
alr alr let me try this should i add the if (receiver.subscriptions.has(user_id)) return; to it?
pat
patβ€’7mo ago
well the idea for that one was when a user starts speaking you don't want to save multiple files
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
what but is that code inside the speaking.on event? i managed to only save one file with the event
pat
patβ€’7mo ago
...edited out bc i was wrong incase you are reading this in the future... receiver.speaking.on('start', userId => { receiver.subscribe(userId, { end: { behavior: EndBehaviorType.Manual } }); });
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
const receiver = connection.receiver;

receiver.speaking.on('start', receiver.subscribe)

for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
const receiver = connection.receiver;

receiver.speaking.on('start', receiver.subscribe)

for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
like that?
pat
patβ€’7mo ago
nevermind, you have to expand it, sad if you have a start+end command, you would subscribe in the start and the for loop in the end command, it will handle ending the streams and creating the files
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
wait so this i have to put it in the end command:
for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
for (const [k, v] of receiver.subscriptions) {
v.push(null);

const oggStream = new prism.opus.OggLogicalBitstream({
opusHead: new prism.opus.OpusHead({
channelCount: 2,
sampleRate: 48000,
}),
pageSizeControl: {
maxPackets: 10,
},
});

const filename = `./recordings/${Date.now()}-${k}.ogg`;

const out = createWriteStream(filename);

pipeline(v, oggStream, out, (err) => {
if (err) {
console.warn(`❌ Error recording file ${filename} - ${err.message}`);
} else {
console.log(`βœ… Recorded ${filename}`);
}
});
}
and this i have to put it in the start command:
const receiver = connection.receiver;

receiver.speaking.on('start', userId => { receiver.subscribe(userId, { end: { behavior: EndBehaviorType.Manual } }); });
const receiver = connection.receiver;

receiver.speaking.on('start', userId => { receiver.subscribe(userId, { end: { behavior: EndBehaviorType.Manual } }); });
pat
patβ€’7mo ago
nah reread the message i sent i edited it because it was wrong
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
oh yeah i knew that i supposed that you wrote receiver.speaking.on('start', receiver.subscribe) to write it faster
pat
patβ€’7mo ago
πŸ’€ totally but yeah I tested using stream.push(null) and it saved to a file when I ran it
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
so I should do that and this
pat
patβ€’7mo ago
yes
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
ive figured out that i get that error when I manually stop the opus stream i guess because i set the end behavior to aftersilence and it actually worked and didn't give me any error also i don't think i will be implementing the code you've gave me because it would change the whole structure of mines and i don't really want that, sorry about that, appreciate your help tho also that error does not really affect the audio so if i don't find a solution for it i would just ignore that specific error tbh
pat
patβ€’7mo ago
alright
π“ͺ𝔂𝓢π“ͺπ“·β›°πŸŒ΄
Uh i guess now my original problem is now solved, im now closing this post Thank you so much for your help pat and qjuh too

Did you find this page helpful?