示例#1
0
        /// <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();
        }
示例#3
0
        /// <summary> Produces Opus encoded audio from PCM samples. </summary>
        /// <param name="input">PCM samples to encode.</param>
        /// <param name="inputOffset">Offset of the frame in pcmSamples.</param>
        /// <param name="output">Buffer to store the encoded frame.</param>
        /// <returns>Length of the frame contained in outputBuffer.</returns>
        public unsafe int EncodeFrame(byte[] input, int inputOffset, byte[] output)
        {
            int result = 0;

            fixed(byte *inPtr = input)
            result = Opus.Encode(_ptr, inPtr + inputOffset, SamplesPerFrame, output, output.Length);

            if (result < 0)
            {
                throw new Exception(((OpusError)result).ToString());
            }

            return(result);
        }
示例#4
0
        internal void PreparePacket(ReadOnlySpan <byte> pcm, ref Memory <byte> target)
        {
            var audioFormat = AudioFormat;

            var packetArray = ArrayPool <byte> .Shared.Rent(Rtp.CalculatePacketSize(audioFormat.SampleCountToSampleSize(audioFormat.CalculateMaximumFrameSize()), SelectedEncryptionMode));

            var packet = packetArray.AsSpan();

            Rtp.EncodeHeader(Sequence, Timestamp, SSRC, packet);
            var opus = packet.Slice(Rtp.HeaderSize, pcm.Length);

            Opus.Encode(pcm, ref opus);

            Sequence++;
            Timestamp += (uint)audioFormat.CalculateFrameSize(audioFormat.CalculateSampleDuration(pcm.Length));

            Span <byte> nonce = new byte[Sodium.NonceSize];

            switch (SelectedEncryptionMode)
            {
            case EncryptionMode.XSalsa20_Poly1305:
                Sodium.GenerateNonce(packet.Slice(0, Rtp.HeaderSize), nonce);
                break;

            case EncryptionMode.XSalsa20_Poly1305_Suffix:
                Sodium.GenerateNonce(nonce);
                break;

            case EncryptionMode.XSalsa20_Poly1305_Lite:
                Sodium.GenerateNonce(Nonce++, nonce);
                break;

            default:
                ArrayPool <byte> .Shared.Return(packetArray);

                throw new Exception("Unsupported encryption mode.");
            }

            Span <byte> encrypted = new byte[Sodium.CalculateTargetSize(opus)];

            Sodium.Encrypt(opus, encrypted, nonce);
            encrypted.CopyTo(packet.Slice(Rtp.HeaderSize));
            packet = packet.Slice(0, Rtp.CalculatePacketSize(encrypted.Length, SelectedEncryptionMode));
            Sodium.AppendNonce(nonce, packet, SelectedEncryptionMode);

            target = target.Slice(0, packet.Length);
            packet.CopyTo(target.Span);
            ArrayPool <byte> .Shared.Return(packetArray);
        }
示例#5
0
        private static void SenderMode(ArgsParser parser)
        {
            if (parser
                .Keys("r", "remote").Value(out var remote, new IPEndPoint(IPAddress.Loopback, 19000))
                .Keys("ssrc").Value(out var ssrc, 0x0000001u)
                .Keys("d", "packet-duration").Value(out var packetDuration, 60u)
                .Keys("i", "input-device").Value(out var device, 0)
                .Result() != null)
            {
                return;
            }

            var rnd       = new Random();
            var seqNumber = (ushort)rnd.Next();
            var timestamp = (uint)rnd.Next();
            var samples   = new short[SAMPLE_RATE / MS_IN_SECOND * packetDuration];
            var marker    = true;

            var codec     = new Opus();
            var rtpSender = new UdpTransport <IPacket>(new RtpContract(), 0);

            rtpSender.Start();

            var waveIn = new WaveInEvent
            {
                DeviceNumber       = device,
                WaveFormat         = new WaveFormat(SAMPLE_RATE, BIT_PER_SAMPLE, CHANNEL_COUNT),
                BufferMilliseconds = (int)packetDuration,
            };

            waveIn.DataAvailable += (sender, e) =>
            {
                var payload = new byte[e.BytesRecorded];
                Buffer.BlockCopy(e.Buffer, 0, samples, 0, e.BytesRecorded);
                Array.Resize(ref payload, codec.Encode(samples, 0, samples.Length, payload, 0, payload.Length));

                var packet = new RtpPacket
                {
                    Ssrc           = ssrc,
                    PayloadType    = PAYLOAD_TYPE_OPUS,
                    Marker         = marker,
                    Timestamp      = timestamp,
                    SequenceNumber = seqNumber,
                    Payload        = payload,
                };

                rtpSender.Send(new PacketContainer <IPacket>
                {
                    Target  = remote,
                    Payload = packet
                });

                timestamp += packetDuration;
                seqNumber += 1;
                marker     = false;
            };
            waveIn.StartRecording();

            Console.WriteLine("Press any key to stop");
            Console.ReadKey();
        }