using System;
using NAudio.Wave.SampleProviders;
// ReSharper disable once CheckNamespace
namespace NAudio.Wave
{
///
/// AudioFileReader simplifies opening an audio file in NAudio
/// Simply pass in the filename, and it will attempt to open the
/// file and set up a conversion path that turns into PCM IEEE float.
/// ACM codecs will be used for conversion.
/// It provides a volume property and implements both WaveStream and
/// ISampleProvider, making it possibly the only stage in your audio
/// pipeline necessary for simple playback scenarios
///
public class AudioFileReader : WaveStream, ISampleProvider
{
private WaveStream readerStream; // the waveStream which we will use for all positioning
private readonly SampleChannel sampleChannel; // sample provider that gives us most stuff we need
private readonly int destBytesPerSample;
private readonly int sourceBytesPerSample;
private readonly long length;
private readonly object lockObject;
///
/// Initializes a new instance of AudioFileReader
///
/// The file to open
public AudioFileReader(string fileName)
{
lockObject = new object();
FileName = fileName;
CreateReaderStream(fileName);
sourceBytesPerSample = (readerStream.WaveFormat.BitsPerSample / 8) * readerStream.WaveFormat.Channels;
sampleChannel = new SampleChannel(readerStream, false);
destBytesPerSample = 4*sampleChannel.WaveFormat.Channels;
length = SourceToDest(readerStream.Length);
}
///
/// Creates the reader stream, supporting all filetypes in the core NAudio library,
/// and ensuring we are in PCM format
///
/// File Name
private void CreateReaderStream(string fileName)
{
if (fileName.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
{
readerStream = new WaveFileReader(fileName);
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
{
readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
readerStream = new BlockAlignReductionStream(readerStream);
}
}
else if (fileName.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
{
if (Environment.OSVersion.Version.Major < 6)
readerStream = new Mp3FileReader(fileName);
else // make MediaFoundationReader the default for MP3 going forwards
readerStream = new MediaFoundationReader(fileName);
}
else if (fileName.EndsWith(".aiff", StringComparison.OrdinalIgnoreCase) || fileName.EndsWith(".aif", StringComparison.OrdinalIgnoreCase))
{
readerStream = new AiffFileReader(fileName);
}
else
{
// fall back to media foundation reader, see if that can play it
readerStream = new MediaFoundationReader(fileName);
}
}
///
/// File Name
///
public string FileName { get; }
///
/// WaveFormat of this stream
///
public override WaveFormat WaveFormat => sampleChannel.WaveFormat;
///
/// Length of this stream (in bytes)
///
public override long Length => length;
///
/// Position of this stream (in bytes)
///
public override long Position
{
get { return SourceToDest(readerStream.Position); }
set { lock (lockObject) { readerStream.Position = DestToSource(value); } }
}
///
/// Reads from this wave stream
///
/// Audio buffer
/// Offset into buffer
/// Number of bytes required
/// Number of bytes read
public override int Read(byte[] buffer, int offset, int count)
{
var waveBuffer = new WaveBuffer(buffer);
int samplesRequired = count / 4;
int samplesRead = Read(waveBuffer.FloatBuffer, offset / 4, samplesRequired);
return samplesRead * 4;
}
///
/// Reads audio from this sample provider
///
/// Sample buffer
/// Offset into sample buffer
/// Number of samples required
/// Number of samples read
public int Read(float[] buffer, int offset, int count)
{
lock (lockObject)
{
return sampleChannel.Read(buffer, offset, count);
}
}
///
/// Gets or Sets the Volume of this AudioFileReader. 1.0f is full volume
///
public float Volume
{
get { return sampleChannel.Volume; }
set { sampleChannel.Volume = value; }
}
///
/// Helper to convert source to dest bytes
///
private long SourceToDest(long sourceBytes)
{
return destBytesPerSample * (sourceBytes / sourceBytesPerSample);
}
///
/// Helper to convert dest to source bytes
///
private long DestToSource(long destBytes)
{
return sourceBytesPerSample * (destBytes / destBytesPerSample);
}
///
/// Disposes this AudioFileReader
///
/// True if called from Dispose
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (readerStream != null) {
readerStream.Dispose();
readerStream = null;
}
}
base.Dispose(disposing);
}
}
}