How to save a user's voice to a local file? (.mp3 or .ogg)

let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

const audioEncoder = new OpusEncoder(48000, 2);
//Save to ./test.ogg ?
});
let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

const audioEncoder = new OpusEncoder(48000, 2);
//Save to ./test.ogg ?
});
completeBuffer definitely holds data, but I tried everything I could find in documentation/forums and still failed to save that buffer's contents as a file. I also tried saving the voiceReceiver stream, and that failed to output a playable audio file as well. Help would be much appreciated!
22 Replies
d.js toolkit
d.js toolkit11mo 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! - Marked as resolved by OP
ThePedroo
ThePedroo11mo ago
Simply pipe to a fs.createWriteStream
KaiFireborn
KaiFirebornOP11mo ago
I tried something similar multiple times, but I failed every time.. What exactly do I need to pipe to and where? (completeBuffer? audioEncoder.encode(completeBuffer)? listener?) Could you please give me an example line of code? This, for example, results in a file that can't be listened to:
let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];
const audioEncoder = new OpusEncoder(48000, 2);

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

const fileStream = fs.createWriteStream("output.ogg");
fileStream.write(completeBuffer);
fileStream.end();
let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];
const audioEncoder = new OpusEncoder(48000, 2);

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

const fileStream = fs.createWriteStream("output.ogg");
fileStream.write(completeBuffer);
fileStream.end();
ThePedroo
ThePedroo11mo ago
Depends, if you want to simply save as is, just listener.pipe(fs.createWriteStream('output.ogg')) should be fine If you're writing only once, don't use createWriteStream, as it's meant to be written multiple times (using streams)
KaiFireborn
KaiFirebornOP11mo ago
If concatting the buffer is okay the way I did it, writing it once should be fine.
ThePedroo
ThePedroo11mo ago
Doesn't have the performance though. Simply piping to it and then just add a listener on "end" would be the best option, and easier too Depends of how much data we're talking about, if it's not much, it should be alright
KaiFireborn
KaiFirebornOP11mo ago
No, not much. It's really only meant to record single sentences, and write the file once the user stops talking. That much works, too. Let me quickly try this to actually write the file though
let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];
// const audioEncoder = new OpusEncoder(48000, 2);

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

listener.pipe(fs.createWriteStream('output.ogg'))
});
let listener = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

const listenBuffer = [];
// const audioEncoder = new OpusEncoder(48000, 2);

listener.on("data", (chunk) => {
listenBuffer.push(chunk);
});

listener.once("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
const completeBuffer = Buffer.concat(listenBuffer);

listener.pipe(fs.createWriteStream('output.ogg'))
});
Did you mean it like this? I think I'm slowly getting too tired to think properly, heheh. In any case, the written file is still unopenable.
ThePedroo
ThePedroo11mo ago
you will want to put the pipe out of the once('end') and remove the data listener Anyway, you can't open because it's not ogg, it's opus
KaiFireborn
KaiFirebornOP11mo ago
Just realised that, too. Do I have to use discord.js/opus to convert it somehow?
ThePedroo
ThePedroo11mo ago
prism-media Only alpha version of it though You can add ogg head, etc
KaiFireborn
KaiFirebornOP11mo ago
Ah I even isntalled an alpha version of that today, but something didn't work out Let me try again, I suppose
ThePedroo
ThePedroo11mo ago
I have a small example, just follow it and it should be fine
KaiFireborn
KaiFirebornOP11mo ago
Oh that's perfect, thank you. I've been looking for similar examples too, but there was some kind of problem every time
ThePedroo
ThePedroo11mo ago
LOL, that one works flawlessly
KaiFireborn
KaiFirebornOP11mo ago
I need [email protected], correct? I now keep getting TypeError: OpusHead is not a constructor.
let opusStream = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

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

let buffer = [];
oggStream.on("data", (chunk) => {
buffer.push(chunk);
});

opusStream.on("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
oggStream.destroy()
fs.createWriteStream('output.ogg').write(Buffer.concat(buffer))
buffer = []
});

opusStream.pipe(oggStream)
let opusStream = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});

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

let buffer = [];
oggStream.on("data", (chunk) => {
buffer.push(chunk);
});

opusStream.on("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
oggStream.destroy()
fs.createWriteStream('output.ogg').write(Buffer.concat(buffer))
buffer = []
});

opusStream.pipe(oggStream)
Pretty sure I did everything just like in the example..
ThePedroo
ThePedroo11mo ago
You need to import prism-media classes OggLogicalBitstream & OpusHead
KaiFireborn
KaiFirebornOP11mo ago
I have, of course const { OggLogicalBitstream, OpusHead } = require("prism-media");
KaiFireborn
KaiFirebornOP11mo ago
Should be the right version, too
No description
ThePedroo
ThePedroo11mo ago
1s
KaiFireborn
KaiFirebornOP11mo ago
I made some changes to how we import from prism-media, installed node-crc, and there seem to be no errors anymore:
const { opus } = require("prism-media");

//other stuff

let opusStream = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});
const oggStream = new opus.OggLogicalBitstream({
opusHead: new opus.OpusHead({
channelCount: 2,
sampleRate: 48000
}),
pageSizeControl: {
maxPackets: 10
}
});

let buffer = [];
oggStream.on("data", (chunk) => {
buffer.push(chunk);
});

opusStream.on("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
oggStream.destroy()
fs.createWriteStream('output.ogg').write(Buffer.concat(buffer))
buffer = []
});

opusStream.pipe(oggStream)
const { opus } = require("prism-media");

//other stuff

let opusStream = voiceReceiver.subscribe(userId, {
end: {
behavior: EndBehaviorType.AfterSilence,
duration: allowedPauseDurationForInputVoice
}
});
const oggStream = new opus.OggLogicalBitstream({
opusHead: new opus.OpusHead({
channelCount: 2,
sampleRate: 48000
}),
pageSizeControl: {
maxPackets: 10
}
});

let buffer = [];
oggStream.on("data", (chunk) => {
buffer.push(chunk);
});

opusStream.on("end", async () => {
logReliably(user.globalName + " stopped speaking. " + getTimeElapsedSinceLastCall());
oggStream.destroy()
fs.createWriteStream('output.ogg').write(Buffer.concat(buffer))
buffer = []
});

opusStream.pipe(oggStream)
However, the written file is still unreadable.
ThePedroo
ThePedroo11mo ago
Maybe because you forgot to .end the writeStream?
KaiFireborn
KaiFirebornOP11mo ago
Oh, you're right! Now it finally works. Thank you so much!
Want results from more Discord servers?
Add your server