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); } } }