bwmarrin / discordgo

(Golang) Go bindings for Discord
BSD 3-Clause "New" or "Revised" License
5.1k stars 812 forks source link

How to send Opus data correctly #1272

Open benstigsen opened 2 years ago

benstigsen commented 2 years ago

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"
rhighs commented 1 year ago

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

RazvanBerbece commented 11 months ago

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 ?

benstigsen commented 11 months ago

I didn't get it to work.