/// <summary> /// Encodes, encrypts, and sends the provided PCM data to the connected voice channel. /// </summary> /// <param name="pcmData">PCM data to encode, encrypt, and send.</param> /// <param name="blockSize">Millisecond length of the PCM data.</param> /// <param name="bitRate">Bitrate of the PCM data.</param> /// <returns>Task representing the sending operation.</returns> public async Task SendAsync(byte[] pcmData, int blockSize, int bitRate = 16) { if (!IsInitialized) { throw new InvalidOperationException("The connection is not initialized"); } await PlaybackSemaphore.WaitAsync().ConfigureAwait(false); var rtp = Rtp.Encode(Sequence, Timestamp, SSRC); var dat = Opus.Encode(pcmData, 0, pcmData.Length, bitRate); dat = Sodium.Encode(dat, Rtp.MakeNonce(rtp), Key); dat = Rtp.Encode(rtp, dat); await SendSpeakingAsync(true).ConfigureAwait(false); // works: //await UdpClient.SendAsync(dat, dat.Length).ConfigureAwait(false); await UdpClient.SendNativelyAsync(dat, dat.Length).ConfigureAwait(false); Sequence++; Timestamp += 48 * (uint)blockSize; PlaybackSemaphore.Release(); }
/// <summary> /// Encodes, encrypts, and sends the provided PCM data to the connected voice channel. /// </summary> /// <param name="pcmData">PCM data to encode, encrypt, and send.</param> /// <param name="blockSize">Millisecond length of the PCM data.</param> /// <param name="bitRate">Bitrate of the PCM data.</param> /// <returns>Task representing the sending operation.</returns> public async Task SendAsync(byte[] pcmData, int blockSize, int bitRate = 16) { if (!IsInitialized) { throw new InvalidOperationException("The connection is not initialized"); } await PlaybackSemaphore.WaitAsync().ConfigureAwait(false); var rtp = Rtp.Encode(Sequence, Timestamp, SSRC); var dat = Opus.Encode(pcmData, 0, pcmData.Length, bitRate); dat = Sodium.Encode(dat, Rtp.MakeNonce(rtp), Key); dat = Rtp.Encode(rtp, dat); if (SynchronizerTicks == 0) { SynchronizerTicks = Stopwatch.GetTimestamp(); SynchronizerResolution = (Stopwatch.Frequency * 0.02); 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); } else { // 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 cts = Math.Max(Stopwatch.GetTimestamp() - SynchronizerTicks, 0); if (cts < SynchronizerResolution) { await Task.Delay(TimeSpan.FromTicks((long)((SynchronizerResolution - cts) * TickResolution))).ConfigureAwait(false); } SynchronizerTicks += SynchronizerResolution; } await SendSpeakingAsync(true).ConfigureAwait(false); await UdpClient.SendAsync(dat, dat.Length).ConfigureAwait(false); Sequence++; Timestamp += 48 * (uint)blockSize; PlaybackSemaphore.Release(); }