Last active
February 21, 2020 23:43
-
-
Save chutchinson/cc2f5596ac68747b6fd6f7286dfe5aae to your computer and use it in GitHub Desktop.
NAudio Opus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Concentus.Structs; | |
using NAudio.Wave; | |
using System; | |
using System.Threading.Tasks; | |
namespace AudioGen | |
{ | |
public class BufferedSampleProvider : ISampleProvider | |
{ | |
private float[] Buffer { get; } | |
private int Position { get; set; } | |
public WaveFormat WaveFormat => | |
WaveFormat.CreateIeeeFloatWaveFormat(48000, 1); | |
public BufferedSampleProvider(float[] buffer) | |
{ | |
Buffer = buffer; | |
} | |
public int Read(float[] buffer, int offset, int count) | |
{ | |
if (Position >= Buffer.Length) | |
{ | |
return 0; | |
} | |
var slice = Buffer.AsSpan().Slice(Position, count); | |
slice.CopyTo(buffer); | |
Position += count; | |
return slice.Length; | |
} | |
} | |
class Program | |
{ | |
static Task PlayAudioAsync(float[] buffer) | |
{ | |
var taskCompletionSource = new TaskCompletionSource<bool>(); | |
var provider = new BufferedSampleProvider(buffer); | |
var wasapi = new WasapiOut(); | |
wasapi.PlaybackStopped += (sender, e) => | |
{ | |
if (e.Exception != null) | |
{ | |
taskCompletionSource.SetException(e.Exception); | |
return; | |
} | |
taskCompletionSource.SetResult(true); | |
}; | |
wasapi.Init(provider); | |
wasapi.Play(); | |
return taskCompletionSource.Task; | |
} | |
static async Task Main(string[] args) | |
{ | |
var channels = 1; | |
var sampleRate = 48000; | |
var frequency = 440.0; | |
var frameSize = 960; | |
var buffer = new float[sampleRate]; | |
var decodedBuffer = new float[sampleRate]; | |
for (var i = 0; i < buffer.Length; i++) | |
{ | |
var time = i / (float)buffer.Length; | |
var sample = Math.Sin(2.0 * Math.PI * frequency * time); | |
buffer[i] = (float)sample; | |
} | |
Console.WriteLine("Reference Tone"); | |
await PlayAudioAsync(buffer); | |
Console.WriteLine("Opus Tone"); | |
Console.ReadKey(); | |
var offset = 0; | |
var packet = new byte[frameSize * sizeof(float)]; | |
var encoder = OpusEncoder.Create(sampleRate, channels, | |
Concentus.Enums.OpusApplication.OPUS_APPLICATION_AUDIO); | |
encoder.Bitrate = 6144; // min | |
encoder.ForceMode = Concentus.Enums.OpusMode.MODE_AUTO; | |
encoder.SignalType = Concentus.Enums.OpusSignal.OPUS_SIGNAL_VOICE; | |
encoder.Complexity = 0; | |
var decoder = new OpusDecoder(sampleRate, channels); | |
var decodeOffset = 0; | |
while (offset < buffer.Length) | |
{ | |
var packetSize = encoder.Encode(buffer, offset, frameSize, packet, 0, packet.Length); | |
var samplesDecoded = decoder.Decode(packet, 0, packetSize, decodedBuffer, decodeOffset, frameSize); | |
offset += frameSize; | |
decodeOffset += samplesDecoded; | |
} | |
await PlayAudioAsync(decodedBuffer); | |
Console.WriteLine("done"); | |
Console.ReadKey(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment