forked from naudio/NAudio
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WaveFormatConversionProvider supports passing an IWaveProvider throug…
…h an ACM codec. WaveFormatConversionStream uses it. Refactored ACM conversion to remove need for repositioning backwards in stream.
- Loading branch information
Showing
6 changed files
with
277 additions
and
187 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
179 changes: 179 additions & 0 deletions
179
NAudio/Wave/WaveStreams/WaveFormatConversionProvider.cs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using NAudio.Wave.Compression; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace NAudio.Wave | ||
{ | ||
/// <summary> | ||
/// IWaveProvider that passes through an ACM Codec | ||
/// </summary> | ||
public class WaveFormatConversionProvider : IWaveProvider, IDisposable | ||
{ | ||
private readonly AcmStream conversionStream; | ||
private readonly IWaveProvider sourceProvider; | ||
private readonly WaveFormat targetFormat; | ||
private readonly int preferredSourceReadSize; | ||
private int leftoverDestBytes; | ||
private int leftoverDestOffset; | ||
private int leftoverSourceBytes; | ||
private bool isDisposed; | ||
/// <summary> | ||
/// Create a new WaveFormat conversion stream | ||
/// </summary> | ||
/// <param name="targetFormat">Desired output format</param> | ||
/// <param name="sourceProvider">Source Provider</param> | ||
public WaveFormatConversionProvider(WaveFormat targetFormat, IWaveProvider sourceProvider) | ||
{ | ||
this.sourceProvider = sourceProvider; | ||
this.targetFormat = targetFormat; | ||
|
||
conversionStream = new AcmStream(sourceProvider.WaveFormat, targetFormat); | ||
|
||
preferredSourceReadSize = Math.Min(sourceProvider.WaveFormat.AverageBytesPerSecond, conversionStream.SourceBuffer.Length); | ||
preferredSourceReadSize -= (preferredSourceReadSize% sourceProvider.WaveFormat.BlockAlign); | ||
} | ||
|
||
/// <summary> | ||
/// Gets the WaveFormat of this stream | ||
/// </summary> | ||
public WaveFormat WaveFormat | ||
{ | ||
get | ||
{ | ||
return targetFormat; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Indicates that a reposition has taken place, and internal buffers should be reset | ||
/// </summary> | ||
public void Reposition() | ||
{ | ||
leftoverDestBytes = 0; | ||
leftoverDestOffset = 0; | ||
leftoverSourceBytes = 0; | ||
conversionStream.Reposition(); | ||
} | ||
|
||
/// <summary> | ||
/// Reads bytes from this stream | ||
/// </summary> | ||
/// <param name="buffer">Buffer to read into</param> | ||
/// <param name="offset">Offset in buffer to read into</param> | ||
/// <param name="count">Number of bytes to read</param> | ||
/// <returns>Number of bytes read</returns> | ||
public int Read(byte[] buffer, int offset, int count) | ||
{ | ||
int bytesRead = 0; | ||
if (count % WaveFormat.BlockAlign != 0) | ||
{ | ||
//throw new ArgumentException("Must read complete blocks"); | ||
count -= (count % WaveFormat.BlockAlign); | ||
} | ||
|
||
while (bytesRead < count) | ||
{ | ||
// first copy in any leftover destination bytes | ||
int readFromLeftoverDest = Math.Min(count - bytesRead, leftoverDestBytes); | ||
if (readFromLeftoverDest > 0) | ||
{ | ||
Array.Copy(conversionStream.DestBuffer, leftoverDestOffset, buffer, offset+bytesRead, readFromLeftoverDest); | ||
leftoverDestOffset += readFromLeftoverDest; | ||
leftoverDestBytes -= readFromLeftoverDest; | ||
bytesRead += readFromLeftoverDest; | ||
} | ||
if (bytesRead >= count) | ||
{ | ||
// we've fulfilled the request from the leftovers alone | ||
break; | ||
} | ||
|
||
// now we'll convert one full source buffer | ||
var sourceReadSize = Math.Min(preferredSourceReadSize, | ||
conversionStream.SourceBuffer.Length - leftoverSourceBytes); | ||
|
||
// always read our preferred size, we can always keep leftovers for the next call to Read if we get | ||
// too much | ||
int sourceBytesRead = sourceProvider.Read(conversionStream.SourceBuffer, leftoverSourceBytes, sourceReadSize); | ||
int sourceBytesAvailable = sourceBytesRead + leftoverSourceBytes; | ||
if (sourceBytesAvailable == 0) | ||
{ | ||
// we've reached the end of the input | ||
break; | ||
} | ||
|
||
int sourceBytesConverted; | ||
int destBytesConverted = conversionStream.Convert(sourceBytesAvailable, out sourceBytesConverted); | ||
if (sourceBytesConverted == 0) | ||
{ | ||
Debug.WriteLine(String.Format("Warning: couldn't convert anything from {0}", sourceBytesAvailable)); | ||
// no point backing up in this case as we're not going to manage to finish playing this | ||
break; | ||
} | ||
leftoverSourceBytes = sourceBytesAvailable - sourceBytesConverted; | ||
|
||
if (leftoverSourceBytes > 0) | ||
{ | ||
// buffer.blockcopy is safe for overlapping copies | ||
Buffer.BlockCopy(conversionStream.SourceBuffer, sourceBytesConverted, conversionStream.SourceBuffer, | ||
0, leftoverSourceBytes); | ||
} | ||
|
||
if (destBytesConverted > 0) | ||
{ | ||
int bytesRequired = count - bytesRead; | ||
int toCopy = Math.Min(destBytesConverted, bytesRequired); | ||
|
||
// save leftovers | ||
if (toCopy < destBytesConverted) | ||
{ | ||
leftoverDestBytes = destBytesConverted - toCopy; | ||
leftoverDestOffset = toCopy; | ||
} | ||
Array.Copy(conversionStream.DestBuffer, 0, buffer, bytesRead + offset, toCopy); | ||
bytesRead += toCopy; | ||
} | ||
else | ||
{ | ||
// possible error here | ||
Debug.WriteLine(string.Format("sourceBytesRead: {0}, sourceBytesConverted {1}, destBytesConverted {2}", | ||
sourceBytesRead, sourceBytesConverted, destBytesConverted)); | ||
//Debug.Assert(false, "conversion stream returned nothing at all"); | ||
break; | ||
} | ||
} | ||
return bytesRead; | ||
} | ||
|
||
/// <summary> | ||
/// Disposes this stream | ||
/// </summary> | ||
/// <param name="disposing">true if the user called this</param> | ||
protected virtual void Dispose(bool disposing) | ||
{ | ||
if (!isDisposed) | ||
{ | ||
isDisposed = true; | ||
conversionStream.Dispose(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Disposes this resource | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
GC.SuppressFinalize(this); | ||
Dispose(true); | ||
} | ||
|
||
/// <summary> | ||
/// Finalizer | ||
/// </summary> | ||
~WaveFormatConversionProvider() | ||
{ | ||
Dispose(false); | ||
} | ||
} | ||
} |
Oops, something went wrong.