discordgo icon indicating copy to clipboard operation
discordgo copied to clipboard

How to send Opus data correctly

Open benstigsen opened this issue 2 years ago • 3 comments

I do see that there's dgVoice and dca, but they have been a mess to get working on Windows.

I have now downloaded Opus tools, which include opusenc, which allows me to convert a .wav to .opus. However, I'm not sure how to send the data correctly... here's what I'm currently doing:

package main

import (
	"os"
	"flag"
	"fmt"
	// "time"
	"bufio"
	"github.com/bwmarrin/discordgo"
)

// Variables used for command line parameters
var (
	Token     string
	ChannelID string
	GuildID   string
)

const (
	channels = 2 // --raw-chan 2
	frameRate = 48000 // --raw-rate 48000
	frameSize = 60 // --framesize 60 (this can't be set to 960 according to opusenc)
	maxBytes = frameSize * 2 * 2
)

func init() {
	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.StringVar(&GuildID, "g", "", "Guild in which voice channel exists")
	flag.StringVar(&ChannelID, "c", "", "Voice channel to connect to")
	flag.Parse()
}

// Read chunks of the .opus file and send
func play(v *discordgo.VoiceConnection) {
	file, err := os.Open("Audiorezout - Anpiel.opus")
	if err != nil {
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	buffer := make([]byte, maxBytes)

	v.Speaking(true)
	defer v.Speaking(false)

	for {
		read, err := reader.Read(buffer)
		if err != nil {
			break
		}

		if !v.Ready || v.OpusSend == nil {
			fmt.Println("Ain't ready")
			return
		}
		
		// time.Sleep(2000)
		v.OpusSend <- buffer[:read]
	}
}

func main() {
	s, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session:", err)
		return
	}
	defer s.Close()

	s.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildVoiceStates)

	err = s.Open()
	if err != nil {
		fmt.Println("error opening connection:", err)
		return
	}

	v, err := s.ChannelVoiceJoin(GuildID, ChannelID, true, false)
	if err != nil {
		fmt.Println("failed to join voice channel:", err)
		return
	}

	play(v)
}

So basically I have frameSize * 2 * 2, which is the amount of bytes I read from "Audiorezout - Anpiel.opus" and send to the Discord voice channel, without any time out because I'm not sure what it should be.

It does send audio, but not pleasantly at all.

I have attached the music I'm trying to use (from Free Music Archive) Audiorezout - Anpiel.zip

ffmpeg -i "Audiorezout - Anpiel.mp3" "Audiorezout - Anpiel.wav"
opusenc --raw-rate 48000 --raw-chan 2 --framesize 60 "Audiorezout - Anpiel.wav" "Audiorezout - Anpiel.opus"

benstigsen avatar Oct 31 '22 15:10 benstigsen

What about setting the frameSize to 960, have a look here

rhighs avatar Feb 11 '23 19:02 rhighs

I do see that there's dgVoice and dca, but they have been a mess to get working on Windows.

I have now downloaded Opus tools, which include opusenc, which allows me to convert a .wav to .opus. However, I'm not sure how to send the data correctly... here's what I'm currently doing:

package main

import (
	"os"
	"flag"
	"fmt"
	// "time"
	"bufio"
	"github.com/bwmarrin/discordgo"
)

// Variables used for command line parameters
var (
	Token     string
	ChannelID string
	GuildID   string
)

const (
	channels = 2 // --raw-chan 2
	frameRate = 48000 // --raw-rate 48000
	frameSize = 60 // --framesize 60 (this can't be set to 960 according to opusenc)
	maxBytes = frameSize * 2 * 2
)

func init() {
	flag.StringVar(&Token, "t", "", "Bot Token")
	flag.StringVar(&GuildID, "g", "", "Guild in which voice channel exists")
	flag.StringVar(&ChannelID, "c", "", "Voice channel to connect to")
	flag.Parse()
}

// Read chunks of the .opus file and send
func play(v *discordgo.VoiceConnection) {
	file, err := os.Open("Audiorezout - Anpiel.opus")
	if err != nil {
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	buffer := make([]byte, maxBytes)

	v.Speaking(true)
	defer v.Speaking(false)

	for {
		read, err := reader.Read(buffer)
		if err != nil {
			break
		}

		if !v.Ready || v.OpusSend == nil {
			fmt.Println("Ain't ready")
			return
		}
		
		// time.Sleep(2000)
		v.OpusSend <- buffer[:read]
	}
}

func main() {
	s, err := discordgo.New("Bot " + Token)
	if err != nil {
		fmt.Println("error creating Discord session:", err)
		return
	}
	defer s.Close()

	s.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsGuildVoiceStates)

	err = s.Open()
	if err != nil {
		fmt.Println("error opening connection:", err)
		return
	}

	v, err := s.ChannelVoiceJoin(GuildID, ChannelID, true, false)
	if err != nil {
		fmt.Println("failed to join voice channel:", err)
		return
	}

	play(v)
}

So basically I have frameSize * 2 * 2, which is the amount of bytes I read from "Audiorezout - Anpiel.opus" and send to the Discord voice channel, without any time out because I'm not sure what it should be.

It does send audio, but not pleasantly at all.

I have attached the music I'm trying to use (from Free Music Archive) Audiorezout - Anpiel.zip

ffmpeg -i "Audiorezout - Anpiel.mp3" "Audiorezout - Anpiel.wav"
opusenc --raw-rate 48000 --raw-chan 2 --framesize 60 "Audiorezout - Anpiel.wav" "Audiorezout - Anpiel.opus"

Hey, have you managed to get this kind of thing working ? Also any chance you programatically converted mp3s to opus and then send them to the stream ?

RazvanBerbece avatar Nov 30 '23 22:11 RazvanBerbece

I didn't get it to work.

benstigsen avatar Dec 01 '23 09:12 benstigsen