using System; using NAudio.Dmo; using System.Diagnostics; // ReSharper disable once CheckNamespace namespace NAudio.Wave { /// /// Wave Stream for converting between sample rates /// public class ResamplerDmoStream : WaveStream { private readonly IWaveProvider inputProvider; private readonly WaveStream inputStream; private readonly WaveFormat outputFormat; private DmoOutputDataBuffer outputBuffer; private DmoResampler dmoResampler; private MediaBuffer inputMediaBuffer; private long position; /// /// WaveStream to resample using the DMO Resampler /// /// Input Stream /// Desired Output Format public ResamplerDmoStream(IWaveProvider inputProvider, WaveFormat outputFormat) { this.inputProvider = inputProvider; inputStream = inputProvider as WaveStream; this.outputFormat = outputFormat; dmoResampler = new DmoResampler(); if (!dmoResampler.MediaObject.SupportsInputWaveFormat(0, inputProvider.WaveFormat)) { throw new ArgumentException("Unsupported Input Stream format", nameof(inputProvider)); } dmoResampler.MediaObject.SetInputWaveFormat(0, inputProvider.WaveFormat); if (!dmoResampler.MediaObject.SupportsOutputWaveFormat(0, outputFormat)) { throw new ArgumentException("Unsupported Output Stream format", nameof(outputFormat)); } dmoResampler.MediaObject.SetOutputWaveFormat(0, outputFormat); if (inputStream != null) { position = InputToOutputPosition(inputStream.Position); } inputMediaBuffer = new MediaBuffer(inputProvider.WaveFormat.AverageBytesPerSecond); outputBuffer = new DmoOutputDataBuffer(outputFormat.AverageBytesPerSecond); } /// /// Stream Wave Format /// public override WaveFormat WaveFormat => outputFormat; private long InputToOutputPosition(long inputPosition) { double ratio = (double)outputFormat.AverageBytesPerSecond / inputProvider.WaveFormat.AverageBytesPerSecond; long outputPosition = (long)(inputPosition * ratio); if (outputPosition % outputFormat.BlockAlign != 0) { outputPosition -= outputPosition % outputFormat.BlockAlign; } return outputPosition; } private long OutputToInputPosition(long outputPosition) { double ratio = (double)outputFormat.AverageBytesPerSecond / inputProvider.WaveFormat.AverageBytesPerSecond; long inputPosition = (long)(outputPosition / ratio); if (inputPosition % inputProvider.WaveFormat.BlockAlign != 0) { inputPosition -= inputPosition % inputProvider.WaveFormat.BlockAlign; } return inputPosition; } /// /// Stream length in bytes /// public override long Length { get { if (inputStream == null) { throw new InvalidOperationException("Cannot report length if the input was an IWaveProvider"); } return InputToOutputPosition(inputStream.Length); } } /// /// Stream position in bytes /// public override long Position { get { return position; } set { if (inputStream == null) { throw new InvalidOperationException("Cannot set position if the input was not a WaveStream"); } inputStream.Position = OutputToInputPosition(value); position = InputToOutputPosition(inputStream.Position); dmoResampler.MediaObject.Discontinuity(0); } } /// /// Reads data from input stream /// /// buffer /// offset into buffer /// Bytes required /// Number of bytes read public override int Read(byte[] buffer, int offset, int count) { int outputBytesProvided = 0; while (outputBytesProvided < count) { if (dmoResampler.MediaObject.IsAcceptingData(0)) { // 1. Read from the input stream int inputBytesRequired = (int)OutputToInputPosition(count - outputBytesProvided); byte[] inputByteArray = new byte[inputBytesRequired]; int inputBytesRead = inputProvider.Read(inputByteArray, 0, inputBytesRequired); if (inputBytesRead == 0) { //Debug.WriteLine("ResamplerDmoStream.Read: No input data available"); break; } // 2. copy into our DMO's input buffer inputMediaBuffer.LoadData(inputByteArray, inputBytesRead); // 3. Give the input buffer to the DMO to process dmoResampler.MediaObject.ProcessInput(0, inputMediaBuffer, DmoInputDataBufferFlags.None, 0, 0); outputBuffer.MediaBuffer.SetLength(0); outputBuffer.StatusFlags = DmoOutputDataBufferFlags.None; // 4. Now ask the DMO for some output data dmoResampler.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new[] { outputBuffer }); if (outputBuffer.Length == 0) { Debug.WriteLine("ResamplerDmoStream.Read: No output data available"); break; } // 5. Now get the data out of the output buffer outputBuffer.RetrieveData(buffer, offset + outputBytesProvided); outputBytesProvided += outputBuffer.Length; Debug.Assert(!outputBuffer.MoreDataAvailable, "have not implemented more data available yet"); } else { Debug.Assert(false, "have not implemented not accepting logic yet"); } } position += outputBytesProvided; return outputBytesProvided; } /// /// Dispose /// /// True if disposing (not from finalizer) protected override void Dispose(bool disposing) { if (inputMediaBuffer != null) { inputMediaBuffer.Dispose(); inputMediaBuffer = null; } outputBuffer.Dispose(); if (dmoResampler != null) { //resampler.Dispose(); s dmoResampler = null; } base.Dispose(disposing); } } }