mirror of
https://github.com/diamondburned/arikawa.git
synced 2024-11-27 17:23:00 +00:00
Voice: Allow custom frame parameters; removed StopSpeaking
This comimt adds a method into the UDP connection to control the internal frequency ticker which controls the speed of playback. For more information, refer to (*voice/udp.Connection).ResetFrequency(). StopSpeaking is removed because it no longer works with variable parameters. The functionality of that method was also arguably useless, as it only sends silent frames.
This commit is contained in:
parent
f4750292eb
commit
6bdac16c2a
|
@ -21,8 +21,6 @@ import (
|
|||
|
||||
const Protocol = "xsalsa20_poly1305"
|
||||
|
||||
var OpusSilence = [...]byte{0xF8, 0xFF, 0xFE}
|
||||
|
||||
// ErrAlreadyConnecting is returned when the session is already connecting.
|
||||
var ErrAlreadyConnecting = errors.New("already connecting")
|
||||
|
||||
|
@ -239,8 +237,7 @@ func (s *Session) reconnectCtx(ctx context.Context) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Speaking tells Discord we're speaking. This calls
|
||||
// (*voicegateway.Gateway).Speaking(). This method should not be called
|
||||
// Speaking tells Discord we're speaking. This method should not be called
|
||||
// concurrently.
|
||||
func (s *Session) Speaking(flag voicegateway.SpeakingFlag) error {
|
||||
s.mut.RLock()
|
||||
|
@ -250,22 +247,6 @@ func (s *Session) Speaking(flag voicegateway.SpeakingFlag) error {
|
|||
return gateway.Speaking(flag)
|
||||
}
|
||||
|
||||
// StopSpeaking sends 5 frames of silence over the UDP connection. Since the UDP
|
||||
// connection itself is not concurrently safe, this method should not be called
|
||||
// as such.
|
||||
func (s *Session) StopSpeaking() error {
|
||||
udp := s.VoiceUDPConn()
|
||||
|
||||
// Send 5 frames of silence.
|
||||
for i := 0; i < 5; i++ {
|
||||
if _, err := udp.Write(OpusSilence[:]); err != nil {
|
||||
return errors.Wrapf(err, "failed to send frame %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UseContext tells the UDP voice connection to write with the given mutex.
|
||||
func (s *Session) UseContext(ctx context.Context) error {
|
||||
s.mut.Lock()
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// Dialer is the default dialer that this package uses for all its dialing.
|
||||
|
@ -27,9 +26,12 @@ type Connection struct {
|
|||
conn net.Conn
|
||||
ssrc uint32
|
||||
|
||||
frequency rate.Limiter
|
||||
packet [12]byte
|
||||
secret [32]byte
|
||||
// frequency rate.Limiter
|
||||
frequency *time.Ticker
|
||||
timeIncr uint32
|
||||
|
||||
packet [12]byte
|
||||
secret [32]byte
|
||||
|
||||
sequence uint16
|
||||
timestamp uint32
|
||||
|
@ -87,15 +89,44 @@ func DialConnectionCtx(ctx context.Context, addr string, ssrc uint32) (*Connecti
|
|||
return &Connection{
|
||||
GatewayIP: string(ip),
|
||||
GatewayPort: port,
|
||||
// 50 sends per second, 960 samples each at 48kHz
|
||||
frequency: *rate.NewLimiter(rate.Every(20*time.Millisecond), 1),
|
||||
context: context.Background(),
|
||||
packet: packet,
|
||||
ssrc: ssrc,
|
||||
conn: conn,
|
||||
frequency: time.NewTicker(20 * time.Millisecond),
|
||||
timeIncr: 960,
|
||||
context: context.Background(),
|
||||
packet: packet,
|
||||
ssrc: ssrc,
|
||||
conn: conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ResetFrequency resets the internal frequency ticker as well as the timestamp
|
||||
// incremental number. For more information, refer to
|
||||
// https://tools.ietf.org/html/rfc7587#section-4.2.
|
||||
//
|
||||
// frameDuration controls the Opus frame duration used by the UDP connection to
|
||||
// control the frequency of packets sent over. 20ms is the default by libopus.
|
||||
//
|
||||
// timestampIncr is the timestamp to increment for each Opus packet. This should
|
||||
// be consistent with th given frameDuration. For the right combination, refer
|
||||
// to the Valid Parameters section below.
|
||||
//
|
||||
// Valid Parameters
|
||||
//
|
||||
// The following table lists the recommended parameters for these variables.
|
||||
//
|
||||
// +---------+-----+-----+------+------+
|
||||
// | Mode | 10 | 20 | 40 | 60 |
|
||||
// +---------+-----+-----+------+------+
|
||||
// | ts incr | 480 | 960 | 1920 | 2880 |
|
||||
// +---------+-----+-----+------+------+
|
||||
//
|
||||
// Note that audio mode is omitted, as it is not recommended. For the full
|
||||
// table, refer to the IETF RFC7587 section 4.2 link above.
|
||||
func (c *Connection) ResetFrequency(frameDuration time.Duration, timeIncr uint32) {
|
||||
c.frequency.Stop()
|
||||
c.frequency = time.NewTicker(frameDuration)
|
||||
c.timeIncr = timeIncr
|
||||
}
|
||||
|
||||
// UseSecret uses the given secret. This method is not thread-safe, so it should
|
||||
// only be used right after initialization.
|
||||
func (c *Connection) UseSecret(secret [32]byte) {
|
||||
|
@ -123,6 +154,7 @@ func (c *Connection) useContext(ctx context.Context) error {
|
|||
}
|
||||
|
||||
func (c *Connection) Close() error {
|
||||
c.frequency.Stop()
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
|
@ -135,12 +167,10 @@ func (c *Connection) Write(b []byte) (int, error) {
|
|||
// given context. It ignores the context inside the connection, but will restore
|
||||
// the deadline after this call is done.
|
||||
func (c *Connection) WriteCtx(ctx context.Context, b []byte) (int, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
ctx := c.context
|
||||
defer c.useContext(ctx) // restore after we're done
|
||||
oldCtx := c.context
|
||||
|
||||
c.conn.SetWriteDeadline(deadline)
|
||||
}
|
||||
c.useContext(ctx)
|
||||
defer c.useContext(oldCtx)
|
||||
|
||||
return c.write(b)
|
||||
}
|
||||
|
@ -151,16 +181,19 @@ func (c *Connection) write(b []byte) (int, error) {
|
|||
c.sequence++
|
||||
|
||||
binary.BigEndian.PutUint32(c.packet[4:8], c.timestamp)
|
||||
c.timestamp += 960 // Samples
|
||||
c.timestamp += c.timeIncr
|
||||
|
||||
copy(c.nonce[:], c.packet[:])
|
||||
|
||||
if err := c.frequency.Wait(c.context); err != nil {
|
||||
return 0, errors.Wrap(err, "failed to wait for frequency tick")
|
||||
}
|
||||
|
||||
toSend := secretbox.Seal(c.packet[:], b, &c.nonce, &c.secret)
|
||||
|
||||
select {
|
||||
case <-c.frequency.C:
|
||||
|
||||
case <-c.context.Done():
|
||||
return 0, c.context.Err()
|
||||
}
|
||||
|
||||
n, err := c.conn.Write(toSend)
|
||||
if err != nil {
|
||||
return n, errors.Wrap(err, "failed to write to UDP connection")
|
||||
|
|
Loading…
Reference in a new issue