This example shows how to stream an Ogg Opus file to a voice channel. This example requires some additional dependencies, namely libogg
and opusfile
.
#include <iomanip>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ogg/ogg.h>
#include <opus/opusfile.h>
int main(
int argc,
char const *argv[]) {
dpp::guild* g = dpp::find_guild(event.command.guild_id);
if (!g->connect_member_voice(event.command.get_issuing_user().id)) {
event.reply("You don't seem to be in a voice channel!");
return;
}
event.
reply(
"Joined your channel!");
dpp::voiceconn* v = event.from->get_voice(event.command.guild_id);
if (!v || !v->voiceclient || !v->voiceclient->is_ready()) {
event.reply("There was an issue with getting the voice channel. Make sure I'm in a voice channel!");
return;
}
ogg_sync_state oy;
ogg_stream_state os;
ogg_page og;
ogg_packet op;
OpusHead header;
char *buffer;
FILE *fd;
fd = fopen("/path/to/opus.ogg", "rb");
fseek(fd, 0L, SEEK_END);
size_t sz = ftell(fd);
ogg_sync_init(&oy);
buffer = ogg_sync_buffer(&oy, sz);
fread(buffer, 1, sz, fd);
ogg_sync_wrote(&oy, sz);
if (ogg_sync_pageout(&oy, &og) != 1) {
fprintf(stderr,"Does not appear to be ogg stream.\n");
exit(1);
}
ogg_stream_init(&os, ogg_page_serialno(&og));
if (ogg_stream_pagein(&os,&og) < 0) {
fprintf(stderr,"Error reading initial page of ogg stream.\n");
exit(1);
}
if (ogg_stream_packetout(&os,&op) != 1) {
fprintf(stderr,"Error reading header packet of ogg stream.\n");
exit(1);
}
if (!(op.bytes > 8 && !memcmp("OpusHead", op.packet, 8))) {
fprintf(stderr,"Not an ogg opus stream.\n");
exit(1);
}
int err = opus_head_parse(&header, op.packet, op.bytes);
if (err) {
fprintf(stderr,"Not a ogg opus stream\n");
exit(1);
}
if (header.channel_count != 2 && header.input_sample_rate != 48000) {
fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n");
exit(1);
}
while (ogg_sync_pageout(&oy, &og) == 1) {
ogg_stream_init(&os, ogg_page_serialno(&og));
if(ogg_stream_pagein(&os,&og)<0) {
fprintf(stderr,"Error reading page of Ogg bitstream data.\n");
exit(1);
}
while (ogg_stream_packetout(&os,&op) != 0) {
if (op.bytes > 8 && !memcmp("OpusHead", op.packet, 8)) {
int err = opus_head_parse(&header, op.packet, op.bytes);
if (err) {
fprintf(stderr,"Not a ogg opus stream\n");
exit(1);
}
if (header.channel_count != 2 && header.input_sample_rate != 48000) {
fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n");
exit(1);
}
continue;
}
if (op.bytes > 8 && !memcmp("OpusTags", op.packet, 8))
continue;
int samples = opus_packet_get_samples_per_frame(op.packet, 48000);
v->voiceclient->send_audio_opus(op.packet, op.bytes, samples / 48);
}
}
ogg_stream_clear(&os);
ogg_sync_clear(&oy);
event.reply("Finished playing the audio file!");
}
});
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_bulk_command_create({ joincommand, playcommand });
}
});
return 0;
}
#include <iomanip>
#include <sstream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <oggz/oggz.h>
dpp::guild* g = dpp::find_guild(event.command.guild_id);
if (!g->connect_member_voice(event.command.get_issuing_user().id)) {
event.reply("You don't seem to be in a voice channel!");
return;
}
event.
reply(
"Joined your channel!");
dpp::voiceconn* v = event.from->get_voice(event.command.guild_id);
if (!v || !v->voiceclient || !v->voiceclient->is_ready()) {
event.reply("There was an issue with getting the voice channel. Make sure I'm in a voice channel!");
return;
}
OGGZ *track_og = oggz_open("/path/to/opus.ogg", OGGZ_READ);
if (!track_og) {
fprintf(stderr, "Error opening file\n");
event.reply("There was an issue opening the file!");
return;
}
oggz_set_read_callback(
track_og, -1,
[](OGGZ *oggz, oggz_packet *packet, long serialno,
void *user_data) {
packet->op.bytes);
return 0;
},
);
while (
v &&
v->voiceclient && !
v->voiceclient->terminating) {
static const constexpr long CHUNK_READ = BUFSIZ * 2;
const long read_bytes = oggz_read(track_og, CHUNK_READ);
if (!read_bytes) {
break;
}
}
oggz_close(track_og);
event.reply("Finished playing the audio file!");
}
});
if (dpp::run_once<struct register_bot_commands>()) {
bot.global_bulk_command_create({ joincommand, playcommand });
}
});
return 0;
}