RangeError when playing audio through opusscript - node.js

I've been attempting to use electron to play sounds through a discord bot. However, I've come across three issues with this.
The first is that the code I'd usually use to play a sound file though discord returns an error stating that I am lacking opus.node and opusscript. This shouldn't be happening, as I have #discordjs/opus installed, and running this same code outside of the electron instance works perfectly fine.
message.member.voice.channel.join().then((connection) => {
const dispatcher = connection.play(ytdl("https://www.youtube.com/watch?v=ZlAU_w7-Xp8", { quality: "highestaudio" }));
dispatcher.on("finish", () => {
console.log("Song is finished.");
});
});
To get around the error, I installed opusscript. However, this is where the second issue comes in. I am able to run the sound - for the first 9 times. After the ninth time, the bot emits the following error:
(node:21064) UnhandledPromiseRejectionWarning: RangeError: offset is out of bounds
at Uint16Array.set (<anonymous>)
at OpusScript.encode (C:\Users\spiralio\Desktop\sb\node_modules\opusscript\index.js:67:16)
at Encoder._encode (C:\Users\spiralio\Desktop\sb\node_modules\prism-media\src\opus\Opus.js:60:25)
at Encoder._transform (C:\Users\spiralio\Desktop\sb\node_modules\prism-media\src\opus\Opus.js:157:30)
at Encoder.Transform._read (_stream_transform.js:191:10)
at Encoder.Transform._write (_stream_transform.js:179:12)
at doWrite (_stream_writable.js:403:12)
at writeOrBuffer (_stream_writable.js:387:5)
at Encoder.Writable.write (_stream_writable.js:318:11)
at VolumeTransformer.ondata (_stream_readable.js:716:22)
This error only occurs if the sound file I'm playing overlaps onto itself 9 times. If I let it finish before this error plays, I can continue playing sounds just fine. However, once the error plays, no matter how long I wait sound no longer plays.
A solution I tried was to have the bot run in the renderer process instead of the main process. However, this prompts me with the issue that the bot can't connect to a voice channel from a browser.

There seem to be a few issues at play here.
Firstly, the root issue here is that we're encoding a file that's too large. I suspect that the videos are snowballing to such a point where we hit a brick wall.
I'd try downloading only the audio to avoid sending files too large (see ytdl options).
message.member.voice.channel.join().then((connection) => {
const dispatcher = connection.play(ytdl("https://www.youtube.com/watch?v=ZlAU_w7-Xp8", { quality: "highestaudio", filter: "audioonly" }));
dispatcher.on("finish", () => {
console.log("Song is finished.");
});
});
Electron not detecting #discordjs/opus seems to be a deeper issue with native modules and your builder. Are you experiencing this in production, or do you have a recreation of your Electron setup somewhere?

Related

Discord js how to run mp3 when i'm connected to voice channel

client.on(Events.MessageCreate, async message => {
if (message.author.id === '1111111111111111111111111111111' && message.content.slice(-1) === '!') {
const guild = message.guild;
const channels = guild.channels.cache;
let channelToPlayIn;
for (const [channelId, channel] of channels) {
if (channel.type === 2 && channel.members.size > 0) {
channelToPlayIn = channel;
break;
}
}
if (channelToPlayIn) {
const connection = joinVoiceChannel({
channelId: channelToPlayIn.id,
guildId: channelToPlayIn.guild.id,
adapterCreator: channelToPlayIn.guild.voiceAdapterCreator,
selfDeaf: false,
});
setTimeout(() => connection.destroy(), 5_000);
}
}
});
Here is the way i connecte the bot in voice channal and this part work but i don't how to run mp3 file while the bot is connected
I tried use createAudioRessource and createAudioPlayer but none of them seems to work when i tried them.
Thanks in advance
Before you implement some new code take some simple measures:
1. Check if the bot has speak permissions enabled. (admin works as well)
2 Make sure that the createAudioResource/createAudioPlayer are ran after the bot is connected to the channel. (assuming you commence the play once join)
3 Make sure that the .mp3 file you are using actually exists and can be played.
What you can do if these approaches do not work
Similar to the discord.js v13 used here (assuming you are on 13 and not 14): https://www.youtube.com/watch?v=riyHsgI2IDs&ab_channel=CodeLyon
Use a url to a video or google drive where you will run the mp3. v13 and v14 do not work well with mp3s that you have on your computer as the bot must grab it from your computer making this inefficient etc.
Also, if you still want to use mp3s from your own computer, simply don't. The best solution is to simply use urls and if discord.js does not work well with mp3s from a someone's own computer and you must instead install a seperate npm music player to grab mp3s and then play it on the computer.
I hope this helps you and anyone else who happens to see this and have a wonderful day. If it doesn't comment what you want in more detail and if you are using v13 or v14. Also, it would help if you could explain why it must be a mp3 file from your computer if it must be.

DiscordJS audio playback with multiple streams

I'm trying to create a discord bot using discord.js that is able to both play music and sound effects.
The bot correctly plays music, but when trying to play a sound effect during music playback, the sound effect never starts/ends.
I have the following code for playing sound effects.
function play(guildID, serverQueue, resource){
const newAudioPlayer = createAudioPlayer();
const oldAudioPlayer = serverQueue.audioPlayer;
const connection = getVoiceConnection(guildID)
oldAudioPlayer.pause();
connection.subscribe(newAudioPlayer);
newAudioPlayer.on(AudioPlayerStatus.Idle, () => {
console.log("Done playing");
connection.subscribe(oldAudioPlayer);
oldAudioPlayer.unpause();
newAudioPlayer.stop();
}).on('error', err => {
console.log("Something went wrong when trying to play sound effect");
console.log(err);
connection.subscribe(oldAudioPlayer);
oldAudioPlayer.unpause();
newAudioPlayer.stop();
})
newAudioPlayer.play(resource);
console.log("Trying to play");
}
I realize that a voice connection can only subscribe to one audioplayer at a time and that an audioplayer can only play one resource at a time. This is why, I create a new temporary audio player called "newAudioPlayer" that will play the short sound effect. I then pause the old audio player and subscribe the connection to the new audio player.
The sound effect is correctly played as long as the oldAudioPlayer has not been used to play a resource before. As soon as the oldAudioPlayer has been used to play a resource, the newAudioPlayer never starts playing the resource. I have checked all the different AudioPlayerStates for the newAudioPlayer, but none of them get triggered.
serverQueue.audioPlayer is initialized when a voice channel is joined, and is always set.
The program does print "Trying to play" but no audio can be heard.
Apparently this is a known issue with discord.js:
https://github.com/discordjs/discord.js/issues/7232
The workaround is to play the local file as a stream if the file for the other audioPlayer is also reading a stream.

Discord.js voice stop playing audio after 10 consecutive files

I am trying to do the simple task of playing a single MP3 file when a command is run. The file is stored locally, and I have FFmpeg installed on my computer. The code below is part of my command's file:
const Discord = require("discord.js");
const fs = require('fs');
const { Client, RichEmbed } = require('discord.js');
const config = require("../config.json");
let playing = undefined;
let connection = undefined;
module.exports.run = async (client, message, args, config) => {
if (playing) playing.end()
if (connection == undefined) await message.member.voice.channel.join().then((c) => {
connection = c;
})
playing = connection.play('./sounds/sound.mp3')
}
(note that this code is heavily narrowed down to single out the issue)
When I run the command the first 9 times, it works perfectly - the file is played, and cuts off if it is already playing. I also want to note that the file is ~2 minutes long. However, once I play the file for exactly the 10th time, the bot stops playing audio entirely - as long as all 10 times are overlapping (meaning I don't let the audio finish).
What's more confusing is that if an error is passed after the bot stops playing audio, it appears in an entirely different format than the standard Discord.js errors. For example, this code does not test to see if the user is in a voice channel, so if I purposefully crash the bot by initiating the command without being in a voice channel (after running the command 10 times), the error looks like this:
abort(RangeError: offset is out of bounds). Build with -s ASSERTIONS=1 for more info.
(Use `electron --trace-uncaught ...` to show where the exception was thrown)
(Preceded by a bunch of unformatted code) This however, is not consistent. It seems to only appear after letting the files run entirely.
The issue only fixes itself when the entire bot restarts. Any help would be appreciated.

Record audio on frontend send to backend for Google Speech API call

I am trying to build what I thought was simple functionality into a website that I am building with ReactJS and Express for backend. But it's turning out to be much more difficult than I thought at first.
Essentially what I want is to have a button on the frontend that begins and ends a recording of the user's voice. Then I'd like to take that audio and send it to the server from which I make an API call to Google's Speech2Text API to get the text. My problem is two-fold, first of all which library do you recommend in order to do the recording? I used RecorderJS at first but after following the steps and converting the data into what I thought was the right format (16-signed bit linear PCM) when I send it and save it I get garbage and not what I recorded. Now I am trying to use the node-recorder-lpcm16 library which records with precisely the encoding that I need, however I am not sure if I am running it correctly since in the docs they write them for node such as
recorder = require('node-recorder-lpcm16');
recorder.record();
But what I do in my React code is:
import recorder from 'node-record-lpcm16';
...
onButtonPress() {
recorder.record();
And I am not getting an error on the import statement, but I am getting an error on the record() call. The error that I am getting is Uncaught Error: No such recorder found: sox, but in my config I am not passing sox as the recordProgram but 'rec', not to mention I have sox installed both with brew install sox and with npm install sox in the frontend directory.
Secondly, I am not 100% the best way to send the audio to the server. Right now I am taking my buffer, sending it as a comma-separated string using a POST request; and on the server side splitting the string by commas and casting it as the correct array. However this isn't viable for longer recordings and I am wandering if there is a way I can stream it somehow, and what libs or tools I should use for this. For example in the google speech docs they have an example using the node-lpcm library that goes like this:
const recorder = require('node-record-lpcm16');
const speech = require('#google-cloud/speech');
const client = new speech.SpeechClient();
const recognizeStream = client
.streamingRecognize(request)
.on('error', console.error)
.on('data', data => {whatever});
recorder
.record({
sampleRateHertz: sampleRateHertz,
threshold: 0,
recordProgram: 'rec'
})
.stream()
.on('error', console.error)
.pipe(recognizeStream);
And I am wondering how I set up this type of piping from client to server? Do I have to send a POST request each time on('data') or is there a smarter way of doing this?
Sorry for the length and thanks for any help.

ionic 2 how to play sound effects

I'm actually developping an application with Ionic 2 / angular2.
It's an app to learn english tenses that runs with a SQLite database.
I would like to add a background sound that plays in loop all the time.
The users can exercice themselves with a quizz. I would like to play sound effects when the user submit his answer.
Two different sounds : one for good and one for bad answers.
I've already tried with Nativeaudio, angular-audio and Ionic audio modules but each times the documentation is based on javascript and not typescript, or it is not helpfull.
With native audio, I've succeed once playing the background sound but after it didn't work at all and came up with an error : EXCEPTION: Uncaught (in promise): A reference does not exist for the specified audio id.
For the other solutions (angular-audio and ionic-audio) either i didn't get how to install it either, once installed, I had nothing : no sound and no error.
Thank you very much for your help.
Install:
$ ionic plugin add --save cordova-plugin-nativeaudio
$ npm install --save #ionic-native/native-audio
Usage:
import { NativeAudio } from '#ionic-native/native-audio';
constructor(private nativeAudio: NativeAudio) { }
...
this.nativeAudio.preloadSimple('uniqueId1', 'path/to/file.mp3').then(onSuccess, onError);
this.nativeAudio.preloadComplex('uniqueId2', 'path/to/file2.mp3', 1, 1, 0).then(onSuccess, onError);
this.nativeAudio.play('uniqueId1').then(onSuccess, onError);
// can optionally pass a callback to be called when the file is done playing
this.nativeAudio.play('uniqueId1', () => console.log('uniqueId1 is done playing'));
Reference:
https://ionicframework.com/docs/native/native-audio/

Resources