Sending custom sound (Advanced)
First of, let me issue a warning. This page is for if you want to implement your own sound provider. If all you’re interested in is sending music or similar, take a look at the lavaplayer module. The recommended way to use audio with AckCord at this point is through the lavaplayer module. However, if you want to create a custom implementation, read on.
If you want to send sound with AckCord, you currently have to set up and wire up most of the actors yourself. AckCord still hides the details of the WebSocket and UDP connection though. AckCord assumes that you are familiar with how sound works in Discord. If you are not familiar with this, read the Discord docs for voice connections
Establishing a connection
The first thing you need to do is to tell Discord that you want to begin speaking and sending audio. You do this by sending a
VoiceStateUpdate message to the gateway. From there you wait for two events.
APIMessage.VoiceStateUpdate which gives you access to your
VoiceState and as such your
APIMessage.VoiceServerUpdate which gives you access to the token and endpoint. Once you have those 3, you want to create and login a voice WebSocket connection. To do this, create a
VoiceWsHandler actor, and send it the
Login message. When creating the actor, you have to decide which actor will receive the audio API messages. You can also specify another actor who will be the actor that receives the audio (I would recommend a dedicated actor with a pinned dispatcher(akka.io.pinned-dispatcher) for the actor that receives the audio).
Sending the data
Once you have the WS actor set up, you should expect an
AudioAPIMessage.Ready message sent to the actor which you specified should receive all the audio API messages. This message contains the
UDPHandler actor, which is where you want to send your data. I would recommend setting up an actor just for sending the data. Before sending any data, you always have to send a
SetSpeaking(true) message to the WS handler.
The naive standard way
To send an audio packet, you use the
SendData message. You probably want to use a pinned dispatcher(akka.io.pinned-dispatcher) if you choose to use this method. Even with a pinned dispatcher, however, you still need to remember that the akka timers, while lightweight, are not the most accurate. There is, however, a better way, although it requires a bit more state keeping and setup.
Utilizing the buffer better
UDPHandler actor comes packaged with a buffer. If it cannot send a message this instant, it sends the message as soon possible. Taking advantage of this, you can send one big message containing multiple packets every says 200 ms.
First, send a
BeginBurstMode message to the UDP handler. From here on, the UDP handler sends a
DataRequest packet to the sender of the
BeginBurstMode whenever it reaches a certain threshold from the config. When your actor receives a
DataRequest message from the UDP handler, it should send a
SendDataBurst in the close future. Do note that it does not have to be now, and it should not be now all the time.
If just sending a burst whenever requested, this would be easy. However, you also need to keep track of how much audio you have sent in advance, and send more audio, or wait with sending audio until later based on that information. For example, if we send 12 packets of sound totalling 240ms, and then 30 ms after that receive another request for 12 packets of audio, we might want to wait a bit before sending those packets, to be sure we are not too far ahead.
Other things to keep in mind
If you follow the advice above, creating an audio sender should hopefully not be too hard. One other thing you need to remember is that whenever you stop speaking, you need to send five packets of silence, use the code
udpHandler ! SendDataBurst(Seq.fill(5)(silence)) to easily do this. If you need a reference to how the different types of data senders might look like, you can look at AudioSender and BurstingAudioSender in the lavaplayer module. If you are looking for a good Discord music library, LavaPlayer is probably your best bet.