public void Decode(Stream input, Stream output, bool resample) { int resampleRate = 44100; using OggReader reader = new OggReader(input); using BinaryWriter writer = new BinaryWriter(output, System.Text.Encoding.ASCII, true); using var decoder = OpusDecoder.Create(48000, reader.Channels); LibResampler resampler = null; if (resample) { resampler = new LibResampler(48000, resampleRate, reader.Channels); } bool isFirst = true; long headerPosition = output.Position; output.Position += 44; Span <float> inputBuffer = default; while (!reader.IsStreamFinished) { var frame = reader.ReadPacket(); if (isFirst) { inputBuffer = new float[decoder.GetSamples(frame.Span) * decoder.OutputChannels]; } decoder.DecodeFloat(frame.Span, inputBuffer, out int dataLength); var outputSamples = inputBuffer.Slice(0, dataLength); if (isFirst) { //remove preskip outputSamples = outputSamples.Slice(reader.Preskip); isFirst = false; } Span <float> outputSpan = outputSamples; if (resample) { float[] resampledSamples = new float[resampler.ResampleUpperBound(outputSamples.Length)]; resampler.Resample(outputSamples, resampledSamples, reader.IsStreamFinished, out int outputLength); outputSpan = resampledSamples.AsSpan(0, outputLength); } foreach (float sample in outputSpan) { writer.Write(WaveWriter.ConvertSample(sample)); } } long endPosition = output.Position; output.Position = headerPosition; WaveWriter.WriteWAVHeader(output, reader.Channels, (int)(endPosition - headerPosition), resample ? resampleRate : 48000, 16); output.Position = endPosition; }
public void Encode(Stream input, Stream output) { using var bufferedWaveInput = new BufferedStream(input); using var wav = new WaveReader(bufferedWaveInput); byte channels = (byte)wav.Channels; int resampleRate = wav.SampleRate < 24000 ? 24000 : 48000; using var opus = External.Opus.OpusEncoder.Create(resampleRate, channels, channels > 1 ? Application.Audio : Application.Voip); using var wrapper = new OggWrapper(output, channels, (ushort)opus.LookaheadSamples, true); using var resampler = new LibResampler(wav.SampleRate, resampleRate, channels); opus.Bitrate = channels > 1 ? Core.Settings.OpusMusicBitrate : Core.Settings.OpusVoiceBitrate; int rawSampleCount = (int)Math.Round(resampleRate * Core.Settings.OpusFrameSize); int samplesPerFrame = rawSampleCount * channels; using var tempFloatBuffer = MemoryPool <float> .Shared.Rent(8192); Span <float> tempFloatBufferSpan = tempFloatBuffer.Memory.Span; using var tempByteBuffer = MemoryPool <byte> .Shared.Rent(8192); Span <byte> tempByteBufferSpan = tempByteBuffer.Memory.Span; int outputSamplesUpperBound = resampler.ResampleUpperBound(samplesPerFrame); using var outputSampleBuffer = MemoryPool <float> .Shared.Rent(outputSamplesUpperBound * 10); Span <float> outputSampleSpan = outputSampleBuffer.Memory.Span.Slice(0, outputSamplesUpperBound * 10); int outputSpanStackPointer = 0; bool AppendToOutputSpan(Span <float> inputSampleSpan, Span <float> outputSampleSpan) { int result = wav.Read(inputSampleSpan); bool lastBuffer = false; if (result < inputSampleSpan.Length) { lastBuffer = true; int newSize = result - (result % channels); inputSampleSpan = inputSampleSpan.Slice(0, newSize); } resampler.Resample(inputSampleSpan, outputSampleSpan.Slice(outputSpanStackPointer), lastBuffer, out var outputLength); outputSpanStackPointer += outputLength; return(!lastBuffer); } void MoveOutputToEncoded(bool lastFrame, Span <float> outputSampleSpan, Span <byte> encodedSampleSpan) { if (lastFrame) { outputSampleSpan.Slice(outputSpanStackPointer, samplesPerFrame - outputSpanStackPointer).Clear(); // TODO: Tweak the frame size for end frames to minimize silent time } opus.Encode(outputSampleSpan.Slice(0, samplesPerFrame), encodedSampleSpan, rawSampleCount, out int outlen); wrapper.WritePacket(tempByteBuffer.Memory.Slice(0, outlen), rawSampleCount, lastFrame); if (!lastFrame) { outputSampleSpan.Slice(samplesPerFrame, outputSpanStackPointer - samplesPerFrame).CopyTo(outputSampleSpan); outputSpanStackPointer -= samplesPerFrame; } } while (true) { bool lastFrame = false; while (outputSpanStackPointer < samplesPerFrame && !lastFrame) { lastFrame = !AppendToOutputSpan(tempFloatBufferSpan, outputSampleSpan); } while (outputSpanStackPointer >= samplesPerFrame) { MoveOutputToEncoded(false, outputSampleSpan, tempByteBufferSpan); } if (lastFrame) { MoveOutputToEncoded(true, outputSampleSpan, tempByteBufferSpan); break; } } }