MVH/NAudio-2.2.1/NAudio/AudioFileReader.cs
2024-06-07 00:47:07 +02:00

169 lines
6.4 KiB
C#

using System;
using NAudio.Wave.SampleProviders;
// ReSharper disable once CheckNamespace
namespace NAudio.Wave
{
/// <summary>
/// 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
/// </summary>
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;
/// <summary>
/// Initializes a new instance of AudioFileReader
/// </summary>
/// <param name="fileName">The file to open</param>
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);
}
/// <summary>
/// Creates the reader stream, supporting all filetypes in the core NAudio library,
/// and ensuring we are in PCM format
/// </summary>
/// <param name="fileName">File Name</param>
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);
}
}
/// <summary>
/// File Name
/// </summary>
public string FileName { get; }
/// <summary>
/// WaveFormat of this stream
/// </summary>
public override WaveFormat WaveFormat => sampleChannel.WaveFormat;
/// <summary>
/// Length of this stream (in bytes)
/// </summary>
public override long Length => length;
/// <summary>
/// Position of this stream (in bytes)
/// </summary>
public override long Position
{
get { return SourceToDest(readerStream.Position); }
set { lock (lockObject) { readerStream.Position = DestToSource(value); } }
}
/// <summary>
/// Reads from this wave stream
/// </summary>
/// <param name="buffer">Audio buffer</param>
/// <param name="offset">Offset into buffer</param>
/// <param name="count">Number of bytes required</param>
/// <returns>Number of bytes read</returns>
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;
}
/// <summary>
/// Reads audio from this sample provider
/// </summary>
/// <param name="buffer">Sample buffer</param>
/// <param name="offset">Offset into sample buffer</param>
/// <param name="count">Number of samples required</param>
/// <returns>Number of samples read</returns>
public int Read(float[] buffer, int offset, int count)
{
lock (lockObject)
{
return sampleChannel.Read(buffer, offset, count);
}
}
/// <summary>
/// Gets or Sets the Volume of this AudioFileReader. 1.0f is full volume
/// </summary>
public float Volume
{
get { return sampleChannel.Volume; }
set { sampleChannel.Volume = value; }
}
/// <summary>
/// Helper to convert source to dest bytes
/// </summary>
private long SourceToDest(long sourceBytes)
{
return destBytesPerSample * (sourceBytes / sourceBytesPerSample);
}
/// <summary>
/// Helper to convert dest to source bytes
/// </summary>
private long DestToSource(long destBytes)
{
return sourceBytesPerSample * (destBytes / destBytesPerSample);
}
/// <summary>
/// Disposes this AudioFileReader
/// </summary>
/// <param name="disposing">True if called from Dispose</param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (readerStream != null) {
readerStream.Dispose();
readerStream = null;
}
}
base.Dispose(disposing);
}
}
}