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