/// <summary>
        /// Sends a speaking status to the connected voice channel.
        /// </summary>
        /// <param name="speaking">Whether the current user is speaking or not.</param>
        /// <returns>A task representing the sending operation.</returns>
        public async Task SendSpeakingAsync(bool speaking = true)
        {
            if (!IsInitialized)
            {
                throw new InvalidOperationException("The connection is not initialized");
            }

            if (!speaking)
            {
                var nullpcm = new byte[3840];
                for (var i = 0; i < 5; i++)
                {
                    await SendAsync(nullpcm, 20).ConfigureAwait(false);
                }

                SynchronizerTicks = 0;
                if (PlayingWait != null)
                {
                    PlayingWait.SetResult(true);
                }
            }
            else
            {
                if (PlayingWait == null || PlayingWait.Task.IsCompleted)
                {
                    PlayingWait = new TaskCompletionSource <bool>();
                }
            }

            var pld = new VoiceDispatch
            {
                OpCode  = 5,
                Payload = new VoiceSpeakingPayload
                {
                    Speaking = speaking,
                    Delay    = 0
                }
            };

            var plj = JsonConvert.SerializeObject(pld, Formatting.None);

            VoiceWs.SendMessage(plj);
        }
Esempio n. 2
0
        private async Task VoiceSenderTask()
        {
            var token  = SenderToken;
            var client = UdpClient;
            var queue  = PacketQueue;

            var synchronizerTicks      = (double)Stopwatch.GetTimestamp();
            var synchronizerResolution = (Stopwatch.Frequency * 0.005);
            var tickResolution         = 10_000_000.0 / Stopwatch.Frequency;

            Discord.DebugLogger.LogMessage(LogLevel.Debug, "VoiceNext", $"Timer accuracy: {Stopwatch.Frequency.ToString("#,##0", CultureInfo.InvariantCulture)}/{synchronizerResolution.ToString(CultureInfo.InvariantCulture)} (high resolution? {Stopwatch.IsHighResolution})", DateTime.Now);

            while (!token.IsCancellationRequested)
            {
                var hasPacket = queue.TryDequeue(out var packet);

                byte[] packetArray = null;
                if (hasPacket)
                {
                    if (PlayingWait == null || PlayingWait.Task.IsCompleted)
                    {
                        PlayingWait = new TaskCompletionSource <bool>();
                    }

                    packetArray = packet.Bytes.ToArray();
                }

                // Provided by Laura#0090 (214796473689178133); this is Python, but adaptable:
                //
                // delay = max(0, self.delay + ((start_time + self.delay * loops) + - time.time()))
                //
                // self.delay
                //   sample size
                // start_time
                //   time since streaming started
                // loops
                //   number of samples sent
                // time.time()
                //   DateTime.Now

                var durationModifier = hasPacket ? packet.MillisecondDuration / 5 : 4;
                var cts = Math.Max(Stopwatch.GetTimestamp() - synchronizerTicks, 0);
                if (cts < synchronizerResolution * durationModifier)
                {
                    await Task.Delay(TimeSpan.FromTicks((long)(((synchronizerResolution * durationModifier) - cts) * tickResolution))).ConfigureAwait(false);
                }

                synchronizerTicks += synchronizerResolution * durationModifier;

                if (!hasPacket)
                {
                    continue;
                }

                SendSpeaking(true);
                await UdpClient.SendAsync(packetArray, packetArray.Length).ConfigureAwait(false);

                if (!packet.IsSilence && queue.Count == 0)
                {
                    var nullpcm = new byte[AudioFormat.CalculateSampleSize(20)];
                    for (var i = 0; i < 3; i++)
                    {
                        var nullpacket    = new byte[nullpcm.Length];
                        var nullpacketmem = nullpacket.AsMemory();

                        PreparePacket(nullpcm, ref nullpacketmem);
                        EnqueuePacket(new VoicePacket(nullpacketmem, 20, true));
                    }
                }
                else if (queue.Count == 0)
                {
                    SendSpeaking(false);
                    PlayingWait?.SetResult(true);
                }
            }
        }