Ejemplo n.º 1
0
        /// <summary>
        /// Reads the next packet from the Ogg stream and decodes it, returning the decoded PCM buffer.
        /// If there are no more packets to decode, this returns NULL. If an error occurs, this also returns
        /// NULL and puts the error message into the LastError field
        /// </summary>
        /// <returns>The decoded audio for the next packet in the stream, or NULL</returns>
        public short[] DecodeNextPacket()
        {
            if (_decoder == null)
            {
                throw new InvalidOperationException("Cannot decode opus packets as a decoder was never provided");
            }

            if (_nextDataPacket == null || _nextDataPacket.Length == 0)
            {
                _endOfStream = true;
            }

            if (_endOfStream)
            {
                return(null);
            }

            try
            {
                int     numSamples = OpusPacketInfo.GetNumSamples(_nextDataPacket, 0, _nextDataPacket.Length, _decoder.SampleRate);
                short[] output     = new short[numSamples * _decoder.NumChannels];
                _decoder.Decode(_nextDataPacket, 0, _nextDataPacket.Length, output, 0, numSamples, false);
                QueueNextPacket();
                return(output);
            }
            catch (OpusException e)
            {
                _lastError = "Opus decoder threw exception: " + e.Message;
                return(null);
            }
        }
Ejemplo n.º 2
0
        public void Play()
        {
            BasePlayer.Play();
            playing = true;

            provideThread = new Thread(() =>
            {
                try
                {
                    WebClient wc = new WebClient();
                    wc.Headers[HttpRequestHeader.UserAgent] = Globals.USER_AGENT;

                    using (var stream = wc.OpenRead(url))
                    {
                        var readFullyStream = new ReadFullyStream(stream);

                        int packetCounter = 0;
                        while (playing)
                        {
                            byte[][] packets = ogg.GetAudioPackets(readFullyStream);

                            packetCounter++;
                            //Skip first 5 pages (control frames, etc)
                            if (packetCounter <= 5)
                            {
                                continue;
                            }

                            for (int i = 0; i < packets.Length; i++)
                            {
                                var streamBytes = packets[i];
                                try
                                {
                                    int frameSize     = OpusPacketInfo.GetNumSamplesPerFrame(streamBytes, 0, Globals.SAMPLE_RATE); //Get frame size from opus packet
                                    short[] rawBuffer = new short[frameSize * 2];                                                  //2 channels
                                    var buffer        = decoder.Decode(streamBytes, 0, streamBytes.Length, rawBuffer, 0, frameSize, false);
                                    BasePlayer.QueueBuffer(rawBuffer);

                                    if (visualiser != null)
                                    {
                                        visualiser.AddSamples(rawBuffer);
                                    }
                                }
                                catch (Concentus.OpusException)
                                {
                                    //Skip this frame
                                }
                            }
                        }
                    }
                }
                catch (Exception)
                {
                }
            });
            provideThread.Start();
        }
Ejemplo n.º 3
0
        public void Play()
        {
            audioPlayer.Play();
            playing = true;

            provideThread = new Thread(() =>
            {
                try
                {
                    HttpWebRequest req = WebRequest.CreateHttp(url);
                    req.UserAgent      = Globals.USER_AGENT;

                    using (var stream = req.GetResponse().GetResponseStream())
                    {
                        var readFullyStream = new ReadFullyStream(stream);

                        while (playing)
                        {
                            byte[][] packets = ogg.GetAudioPackets(readFullyStream);

                            for (int i = 0; i < packets.Length; i++)
                            {
                                var streamBytes = packets[i];
                                try
                                {
                                    int frameSize     = OpusPacketInfo.GetNumSamplesPerFrame(streamBytes, 0, Globals.SAMPLE_RATE); //Get frame size from opus packet
                                    short[] rawBuffer = new short[frameSize * 2];                                                  //2 channels
                                    var buffer        = decoder.Decode(streamBytes, 0, streamBytes.Length, rawBuffer, 0, frameSize, false);
                                    audioPlayer.QueueBuffer(rawBuffer);

                                    if (visualiser != null)
                                    {
                                        visualiser.AddSamples(rawBuffer);
                                    }
                                }
                                catch (Concentus.OpusException)
                                {
                                    //Skip this frame
                                    //Note: the first 2 frames will hit this exception (I'm pretty sure they're not audio data frames)
                                }
                            }
                        }
                    }
                } catch (Exception)
                {
                }
            });
            provideThread.Start();
        }
Ejemplo n.º 4
0
        private ResultCode GetPacketNumSamples(out int numSamples, byte[] packet)
        {
            int result = OpusPacketInfo.GetNumSamples(_decoder, packet, 0, packet.Length);

            numSamples = result;

            if (result == OpusError.OPUS_INVALID_PACKET)
            {
                return(ResultCode.OpusInvalidInput);
            }
            else if (result == OpusError.OPUS_BAD_ARG)
            {
                return(ResultCode.OpusInvalidInput);
            }

            return(ResultCode.Success);
        }
Ejemplo n.º 5
0
        private void OnDataReceived(byte[] audioData)
        {
            int frameSize = OpusPacketInfo.GetNumSamples(decoder, audioData, 0, audioData.Length); // must be same as framesize used in input, you can use OpusPacketInfo.GetNumSamples() to determine this dynamically

            short[] outputBuffer = new short[frameSize];

            int thisFrameSize = decoder.Decode(audioData, 0, audioData.Length, outputBuffer, 0, frameSize);

            byte[] decoded = new byte[thisFrameSize * 2];

            for (int i = 0; i < thisFrameSize; i++)
            {
                var data = BitConverter.GetBytes(outputBuffer[i]);
                decoded[i]     = data[0];
                decoded[i + 1] = data[1];
            }

            waveProvider.AddSamples(decoded, 0, decoded.Length);
        }
Ejemplo n.º 6
0
        public OpusFileStream(string filename)
        {
            int samplesPerPacket;

            this.FileName          = filename;
            this.OpusFile          = new FileStream(filename, FileMode.Open);
            this.Decoder           = new OpusDecoder(DefaultSampleRate, 1);
            this.OpusOggReadStream = new OpusOggReadStream(this.Decoder, this.OpusFile);

            if (!this.OpusOggReadStream.HasNextPacket)
            {
                throw new Exception("No opus packets found");
            }

            this.SampleRate       = this.OpusOggReadStream.InputSampleRate;
            this.FirstPacket      = this.OpusOggReadStream.RetrieveNextPacket();
            this.FramesPerPacket  = OpusPacketInfo.GetNumFrames(this.FirstPacket, 0, this.FirstPacket.Length);
            samplesPerPacket      = OpusPacketInfo.GetNumSamples(this.FirstPacket, 0, this.FirstPacket.Length, (int)this.SampleRate);
            this.PacketDurationMs = (samplesPerPacket * 1000) / (int)this.SampleRate;
        }
Ejemplo n.º 7
0
        public Sound LoadSound(string filepath)
        {
            //Read ogg packets
            FileStream stream = new FileStream(filepath, FileMode.Open);

            byte[][] packets = GetAudioPackets(stream);

            List <byte> pcmBytes = new List <byte>();

            //Decode packets from opus to pcm
            for (int i = 0; i < packets.Length; i++)
            {
                try
                {
                    var     packet    = packets[i];
                    int     frameSize = OpusPacketInfo.GetNumSamplesPerFrame(packet, 0, SAMPLE_RATE); //Get frame size from opus packet
                    short[] rawBuffer = new short[frameSize * 2];                                     //2 channels
                    var     buffer    = decoder.Decode(packet, 0, packet.Length, rawBuffer, 0, frameSize, false);

                    //Convert shorts to bytes
                    byte[] result = new byte[rawBuffer.Length * 2];
                    for (int j = 0; j < rawBuffer.Length; j++)
                    {
                        byte[] val = BitConverter.GetBytes(rawBuffer[j]);
                        Array.Copy(val, 0, result, j * 2, 2);
                    }

                    pcmBytes.AddRange(result);
                }
                catch (Concentus.OpusException e)
                {
                    //Skip this frame
                    //Note: the first 2 frames will hit this exception (they're probably just metadata frames, but i'm too lazy to check)
                }
            }

            decoder.ResetState();
            return(Sound.FromS16LE(pcmBytes.ToArray()));
        }
Ejemplo n.º 8
0
        public AudioChunk Decompress(byte[] inputPacket)
        {
            int frameSize = GetFrameSize();
            
            short[] outputBuffer = new short[frameSize];

            bool lostPacket = new Random().Next(0, 100) < _packetLoss;
            if (!lostPacket)
            {
                // Normal decoding
                _timer.Reset();
                _timer.Start();
                int thisFrameSize = _decoder.Decode(inputPacket, 0, inputPacket.Length, outputBuffer, 0, frameSize, false);
                _timer.Stop();
            }
            else
            {
                // packet loss path
                _timer.Reset();
                _timer.Start();
                int thisFrameSize = _decoder.Decode(null, 0, 0, outputBuffer, 0, frameSize, true);
                _timer.Stop();
            }

            short[] finalOutput = new short[frameSize];
            Array.Copy(outputBuffer, finalOutput, finalOutput.Length);

            // Update statistics
            _statistics.Bitrate = inputPacket.Length * 8 * 48000 / 1024 / frameSize;
            OpusMode curMode = OpusPacketInfo.GetEncoderMode(inputPacket, 0);
            if (curMode == OpusMode.MODE_CELT_ONLY)
            {
                _statistics.Mode = "CELT";
            }
            else if (curMode == OpusMode.MODE_HYBRID)
            {
                _statistics.Mode = "Hybrid";
            }
            else if (curMode == OpusMode.MODE_SILK_ONLY)
            {
                _statistics.Mode = "SILK";
            }
            else
            {
                _statistics.Mode = "Unknown";
            }
            _statistics.DecodeSpeed = _frameSize / ((double)_timer.ElapsedTicks / Stopwatch.Frequency * 1000);
            OpusBandwidth curBandwidth = OpusPacketInfo.GetBandwidth(inputPacket, 0);
            if (curBandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)
            {
                _statistics.Bandwidth = 8000;
            }
            else if (curBandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)
            {
                _statistics.Bandwidth = 12000;
            }
            else if (curBandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)
            {
                _statistics.Bandwidth = 16000;
            }
            else if (curBandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)
            {
                _statistics.Bandwidth = 24000;
            }
            else
            {
                _statistics.Bandwidth = 48000;
            }

            return new AudioChunk(finalOutput, 48000);
        }
Ejemplo n.º 9
0
        public static TestResults RunTest(TestParameters parameters, short[] inputFile)
        {
            TestResults returnVal = new TestResults();
            // Create Opus encoder
            IntPtr opusEncoder = IntPtr.Zero;
            IntPtr opusError;

            opusEncoder = opus_encoder_create(parameters.SampleRate, parameters.Channels, (int)parameters.Application, out opusError);
            if ((int)opusError != 0)
            {
                returnVal.Message = "There was an error initializing the Opus encoder";
                returnVal.Passed  = false;
                return(returnVal);
            }

            if (parameters.Bitrate > 0)
            {
                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_BITRATE_REQUEST, parameters.Bitrate * 1024);
            }
            opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_COMPLEXITY_REQUEST, parameters.Complexity);
            opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_DTX_REQUEST, parameters.UseDTX ? 1 : 0);
            if (parameters.PacketLossPercent > 0)
            {
                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_PACKET_LOSS_PERC_REQUEST, parameters.PacketLossPercent);
                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_INBAND_FEC_REQUEST, 1);
            }
            if (parameters.ForceMode != OpusMode.MODE_AUTO)
            {
                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_FORCE_MODE_REQUEST, (int)parameters.ForceMode);
            }
            opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_VBR_REQUEST, parameters.UseVBR ? 1 : 0);
            opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_VBR_CONSTRAINT_REQUEST, parameters.ConstrainedVBR ? 1 : 0);

            // Create Opus decoder
            IntPtr opusDecoder = IntPtr.Zero;

            opusDecoder = opus_decoder_create(parameters.DecoderSampleRate, parameters.DecoderChannels, out opusError);
            if ((int)opusError != 0)
            {
                returnVal.Message = "There was an error initializing the Opus decoder";
                returnVal.Passed  = false;
                return(returnVal);
            }

            // Create Concentus encoder
            OpusEncoder concentusEncoder           = null;
            OpusEncoder concentusEncoderWithoutFEC = null;

            try
            {
                concentusEncoder = CreateConcentusEncoder(parameters);

                if (parameters.PacketLossPercent > 0)
                {
                    concentusEncoderWithoutFEC = CreateConcentusEncoder(parameters);
                    concentusEncoderWithoutFEC.UseInbandFEC = (false);
                }
            }
            catch (OpusException e)
            {
                returnVal.Message = "There was an error initializing the Concentus encoder: " + e.Message;
                returnVal.Passed  = false;
                return(returnVal);
            }

            // Create Concentus decoder
            OpusDecoder concentusDecoder;

            try
            {
                concentusDecoder = new OpusDecoder(parameters.DecoderSampleRate, parameters.DecoderChannels);
            }
            catch (OpusException e)
            {
                returnVal.Message = "There was an error initializing the Concentus decoder: " + e.Message;
                returnVal.Passed  = false;
                return(returnVal);
            }

            // Number of paired samples (the audio length)
            int frameSize = (int)(parameters.FrameSize * parameters.SampleRate / 1000);
            // Number of actual samples in the array (the array length)
            int frameSizeStereo        = frameSize * parameters.Channels;
            int decodedFrameSize       = (int)(parameters.FrameSize * parameters.DecoderSampleRate / 1000);
            int decodedFrameSizeStereo = decodedFrameSize * parameters.DecoderChannels;

            returnVal.FrameLength = frameSize;

            int inputPointer = 0;

            byte[]         outputBuffer              = new byte[10000];
            short[]        inputPacket               = new short[frameSizeStereo];
            short[]        opusDecoded               = new short[decodedFrameSizeStereo];
            short[]        concentusDecoded          = new short[decodedFrameSizeStereo];
            int            frameCount                = 0;
            Stopwatch      concentusTimer            = new Stopwatch();
            Stopwatch      opusTimer                 = new Stopwatch();
            Random         random                    = new Random();
            Queue <string> PacketTransmissionPattern = new Queue <string>();

            for (int c = 0; c < 5; c++)
            {
                PacketTransmissionPattern.Enqueue("|");
            }

            byte[] concentusEncoded    = null;
            int    concentusPacketSize = 0;

            try
            {
                try
                {
                    while (inputPointer + frameSizeStereo < inputFile.Length)
                    {
                        returnVal.FrameCount = frameCount;
                        Array.Copy(inputFile, inputPointer, inputPacket, 0, frameSizeStereo);
                        inputPointer += frameSizeStereo;

                        // Should we randomly switch modes?
                        if (parameters.ForceMode != OpusMode.MODE_AUTO && random.NextDouble() < 0.2)
                        {
                            if (random.NextDouble() < 0.5)
                            {
                                concentusEncoder.ForceMode = (OpusMode.MODE_AUTO);
                                if (concentusEncoderWithoutFEC != null)
                                {
                                    concentusEncoderWithoutFEC.ForceMode = (OpusMode.MODE_AUTO);
                                }
                                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_FORCE_MODE_REQUEST, OpusConstants.OPUS_AUTO);
                            }
                            else
                            {
                                concentusEncoder.ForceMode = (parameters.ForceMode);
                                if (concentusEncoderWithoutFEC != null)
                                {
                                    concentusEncoderWithoutFEC.ForceMode = (parameters.ForceMode);
                                }
                                opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_FORCE_MODE_REQUEST, (int)parameters.ForceMode);
                            }
                        }

                        // If bitrate is variable, set it to a random value every few frames
                        if (parameters.Bitrate < 0 && random.NextDouble() < 0.1)
                        {
                            int newBitrate = random.Next(6, parameters.ForceMode == OpusMode.MODE_SILK_ONLY ? 40 : 510);
                            concentusEncoder.Bitrate = (newBitrate * 1024);
                            opus_encoder_ctl(opusEncoder, OpusControl.OPUS_SET_BITRATE_REQUEST, newBitrate * 1024);
                        }

                        Pointer <short> inputPacketWithOffset = Pointerize(inputPacket);
                        concentusTimer.Start();
                        // Encode with Concentus
                        concentusPacketSize = concentusEncoder.Encode(inputPacketWithOffset.Data, inputPacketWithOffset.Offset, frameSize, outputBuffer, BUFFER_OFFSET, 10000 - BUFFER_OFFSET);
                        concentusTimer.Stop();

                        if (concentusPacketSize <= 0)
                        {
                            returnVal.Message      = "Invalid packet produced (" + concentusPacketSize + ") (frame " + frameCount + ")";
                            returnVal.Passed       = false;
                            returnVal.FailureFrame = inputPacket;
                            return(returnVal);
                        }
                        concentusEncoded = new byte[concentusPacketSize];
                        Array.Copy(outputBuffer, BUFFER_OFFSET, concentusEncoded, 0, concentusPacketSize);

                        // Encode with Opus
                        byte[] opusEncoded;
                        unsafe
                        {
                            fixed(byte *benc = outputBuffer)
                            {
                                byte[] nextFrameBytes = ShortsToBytes(inputPacket);
                                IntPtr encodedPtr     = new IntPtr((void *)(benc));

                                opusTimer.Start();
                                int opusPacketSize = opus_encode(opusEncoder, nextFrameBytes, frameSize, encodedPtr, 10000);

                                opusTimer.Stop();
                                if (ACTUALLY_COMPARE && opusPacketSize != concentusPacketSize)
                                {
                                    returnVal.Message      = "Output packet sizes do not match (frame " + frameCount + ")";
                                    returnVal.Passed       = false;
                                    returnVal.FailureFrame = inputPacket;
                                    return(returnVal);
                                }
                                opusEncoded = new byte[opusPacketSize];
                                Array.Copy(outputBuffer, opusEncoded, opusPacketSize);
                            }
                        }

                        // Check for encoder parity
                        for (int c = 0; ACTUALLY_COMPARE && c < concentusPacketSize; c++)
                        {
                            if (opusEncoded[c] != concentusEncoded[c])
                            {
                                returnVal.Message      = "Encoded packets do not match (frame " + frameCount + ")";
                                returnVal.Passed       = false;
                                returnVal.FailureFrame = inputPacket;
                                return(returnVal);
                            }
                        }

                        // Ensure that the packet can be parsed back
                        try
                        {
                            Pointer <byte> concentusEncodedWithOffset = Pointerize(concentusEncoded);
                            OpusPacketInfo packetInfo = OpusPacketInfo.ParseOpusPacket(concentusEncodedWithOffset.Data, concentusEncodedWithOffset.Offset, concentusPacketSize);
                        }
                        catch (OpusException e)
                        {
                            returnVal.Message      = "PACKETINFO: " + e.Message + " (frame " + frameCount + ")";
                            returnVal.Passed       = false;
                            returnVal.FailureFrame = inputPacket;
                            return(returnVal);
                        }

                        if (concentusEncoderWithoutFEC != null)
                        {
                            // Encode again without FEC and verify that there is a difference
                            int  packetSizeWithoutFEC = concentusEncoderWithoutFEC.Encode(inputPacket, 0, frameSize, outputBuffer, 0, 10000);
                            bool areEqual             = concentusPacketSize == packetSizeWithoutFEC;
                            if (areEqual)
                            {
                                for (int c = 0; c < concentusPacketSize; c++)
                                {
                                    areEqual = areEqual && outputBuffer[c] == concentusEncoded[c];
                                }
                            }
                            if (areEqual && frameCount > 0)
                            {
                                returnVal.Message      = "Enabling FEC did not change the output packet (frame " + frameCount + ")";
                                returnVal.Passed       = false;
                                returnVal.FailureFrame = inputPacket;
                                return(returnVal);
                            }
                        }
                    }
                }
                catch (OpusException e)
                {
                    returnVal.Message      = "ENCODER: " + e.Message + " (frame " + frameCount + ")";
                    returnVal.Passed       = false;
                    returnVal.FailureFrame = inputPacket;
                    return(returnVal);
                }

                try
                {
                    // Should we simulate dropping the packet?
                    PacketTransmissionPattern.Dequeue();
                    bool droppedPacket = false;
                    if (random.Next(0, 100) < parameters.PacketLossPercent)
                    {
                        droppedPacket = true;
                        PacketTransmissionPattern.Enqueue("X");
                    }
                    PacketTransmissionPattern.Enqueue("O");

                    if (!droppedPacket)
                    {
                        // Decode with Concentus
                        Pointer <byte>  concentusEncodedWithOffset = Pointerize(concentusEncoded);
                        Pointer <short> concentusDecodedWithOffset = Pointerize(concentusDecoded);
                        int             concentusOutputFrameSize   = concentusDecoder.Decode(
                            concentusEncodedWithOffset.Data,
                            concentusEncodedWithOffset.Offset,
                            concentusPacketSize,
                            concentusDecodedWithOffset.Data,
                            concentusDecodedWithOffset.Offset,
                            decodedFrameSize,
                            false);
                        concentusTimer.Start();
                        concentusDecoded = Unpointerize(concentusDecodedWithOffset, concentusDecoded.Length);
                        concentusTimer.Stop();

                        // Decode with Opus
                        unsafe
                        {
                            fixed(short *bdec = opusDecoded)
                            {
                                IntPtr decodedPtr = new IntPtr((void *)(bdec));

                                opusTimer.Start();
                                int opusOutputFrameSize = opus_decode(opusDecoder, concentusEncoded, concentusPacketSize, decodedPtr, decodedFrameSize, 0);

                                opusTimer.Stop();
                            }
                        }
                    }
                    else
                    {
                        bool useFEC = random.NextDouble() > 0.5;
                        // Decode with Concentus FEC
                        concentusTimer.Start();
                        int concentusOutputFrameSize = concentusDecoder.Decode(null, 0, 0, concentusDecoded, 0, decodedFrameSize, useFEC);
                        concentusTimer.Stop();

                        // Decode with Opus FEC
                        unsafe
                        {
                            fixed(short *bdec = opusDecoded)
                            {
                                IntPtr decodedPtr = new IntPtr((void *)(bdec));

                                opusTimer.Start();
                                int opusOutputFrameSize = opus_decode(opusDecoder, null, 0, decodedPtr, decodedFrameSize, useFEC ? 1 : 0);

                                opusTimer.Stop();
                            }
                        }
                    }

                    // Check for decoder parity
                    for (int c = 0; ACTUALLY_COMPARE && c < decodedFrameSizeStereo; c++)
                    {
                        if (opusDecoded[c] != concentusDecoded[c])
                        {
                            returnVal.Message = "Decoded frames do not match (frame " + frameCount + ")";
                            if (parameters.PacketLossPercent > 0)
                            {
                                StringBuilder packetLossPattern = new StringBuilder();
                                foreach (string x in PacketTransmissionPattern)
                                {
                                    packetLossPattern.Append(x);
                                }
                                returnVal.Message += " (Packet loss " + packetLossPattern.ToString() + ")";
                            }
                            returnVal.Passed       = false;
                            returnVal.FailureFrame = inputPacket;
                            return(returnVal);
                        }
                    }
                    frameCount++;
                }
                catch (OpusException e)
                {
                    returnVal.Message      = "DECODER: " + e.Message + " (frame " + frameCount + ")";
                    returnVal.Passed       = false;
                    returnVal.FailureFrame = inputPacket;
                    return(returnVal);
                }
            }
            finally
            {
                opus_encoder_destroy(opusEncoder);
                opus_decoder_destroy(opusDecoder);
            }

            returnVal.Passed          = true;
            returnVal.ConcentusTimeMs = concentusTimer.ElapsedMilliseconds;
            returnVal.OpusTimeMs      = opusTimer.ElapsedMilliseconds;
            returnVal.Message         = "Ok!";

            return(returnVal);
        }
Ejemplo n.º 10
0
        public AudioChunk Decompress(byte[] inputPacket)
        {
            int frameSize = GetFrameSize();

            short[] outputBuffer = new short[frameSize];

            bool lostPacket = new Random().Next(0, 100) < _packetLoss;

            if (!lostPacket)
            {
                // normal decoding
                _timer.Reset();
                _timer.Start();
                unsafe
                {
                    fixed(short *bdec = outputBuffer)
                    {
                        IntPtr decodedPtr    = new IntPtr((byte *)(bdec));
                        int    thisFrameSize = opus_decode(_decoder, inputPacket, inputPacket.Length, decodedPtr, frameSize, 0);
                    }
                }
                _timer.Stop();
            }
            else
            {
                // packet loss path
                _timer.Reset();
                _timer.Start();
                unsafe
                {
                    fixed(short *bdec = outputBuffer)
                    {
                        IntPtr decodedPtr    = new IntPtr((byte *)(bdec));
                        int    thisFrameSize = opus_decode(_decoder, null, 0, decodedPtr, frameSize, 1);
                    }
                }
                _timer.Stop();
            }

            short[] finalOutput = new short[frameSize];
            Array.Copy(outputBuffer, finalOutput, finalOutput.Length);

            // Update statistics
            _statistics.Bitrate = inputPacket.Length * 8 * 48000 / 1024 / frameSize;
            OpusMode curMode = OpusPacketInfo.GetEncoderMode(inputPacket, 0);

            if (curMode == OpusMode.MODE_CELT_ONLY)
            {
                _statistics.Mode = "CELT";
            }
            else if (curMode == OpusMode.MODE_HYBRID)
            {
                _statistics.Mode = "Hybrid";
            }
            else if (curMode == OpusMode.MODE_SILK_ONLY)
            {
                _statistics.Mode = "SILK";
            }
            else
            {
                _statistics.Mode = "Unknown";
            }
            _statistics.DecodeSpeed = _frameSize / ((double)_timer.ElapsedTicks / Stopwatch.Frequency * 1000);

            return(new AudioChunk(finalOutput, 48000));
        }