/// <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); }
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); } } }