Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio reader support streams #929

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions NAudio/AudioFileExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;

namespace NAudio
{
/// <summary>
/// A collection of string extensions for handling audio file extensions
/// </summary>
public static class AudioFileExtensions
{
/// <summary>
/// Converts a sound file extension to an enumeration value
/// </summary>
/// <param name="fileExt">The file extension to convert. Case is ignored. It must include the period ('.').</param>
/// <returns>The enumeration value</returns>
/// <remarks>Null file extensions will return as 'unknown'</remarks>
public static AudioFileFormatEnum GetFormatFromFileExt(this string fileExt)
{
if (fileExt != null)
{
switch (fileExt.ToLower())
{
case ".mp3":
return AudioFileFormatEnum.MP3;
case ".wav":
return AudioFileFormatEnum.WAV;
case ".aif":
return AudioFileFormatEnum.AIFF;
case ".aiff":
return AudioFileFormatEnum.AIFF;
default:
return AudioFileFormatEnum.Unknown;
}
}
else
{
return AudioFileFormatEnum.Unknown;
}
}
}
}
15 changes: 15 additions & 0 deletions NAudio/AudioFileFormatEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace NAudio
{
/// <summary>
/// Enumeration for different audio file formats
/// </summary>
public enum AudioFileFormatEnum
{
WAV,
MP3,
AIFF,
Unknown
}
}
86 changes: 80 additions & 6 deletions NAudio/AudioFileReader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using NAudio.Wave.SampleProviders;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -30,21 +31,40 @@ public AudioFileReader(string fileName)
{
lockObject = new object();
FileName = fileName;
CreateReaderStream(fileName);
CreateReaderStreamFromFileName(fileName);
sourceBytesPerSample = (readerStream.WaveFormat.BitsPerSample / 8) * readerStream.WaveFormat.Channels;
sampleChannel = new SampleChannel(readerStream, false);
destBytesPerSample = 4*sampleChannel.WaveFormat.Channels;
length = SourceToDest(readerStream.Length);
}

/// <summary>
/// Initializes a new instance of AudioFileReader
/// </summary>
/// <param name="stream">The stream containing the audio file data</param>
/// <param name="fileExt">The extension of the audio file, including the period ('.')</param>
public AudioFileReader(Stream stream, string fileExt)
{
lockObject = new object();
FileName = null;
CreateReaderStreamFromStream(stream, fileExt);
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)
private void CreateReaderStreamFromFileName(string fileName)
{
if (fileName.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
var fileExt = Path.GetExtension(fileName);
var fileFormat = fileExt.GetFormatFromFileExt();

if (fileFormat == AudioFileFormatEnum.WAV)
{
readerStream = new WaveFileReader(fileName);
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
Expand All @@ -53,14 +73,14 @@ private void CreateReaderStream(string fileName)
readerStream = new BlockAlignReductionStream(readerStream);
}
}
else if (fileName.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
else if (fileFormat == AudioFileFormatEnum.MP3)
{
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))
else if (fileFormat == AudioFileFormatEnum.AIFF)
{
readerStream = new AiffFileReader(fileName);
}
Expand All @@ -70,8 +90,62 @@ private void CreateReaderStream(string fileName)
readerStream = new MediaFoundationReader(fileName);
}
}

/// <summary>
/// Creates the reader stream, supporting all filetypes in the core NAudio library,
/// and ensuring we are in PCM format
/// </summary>
/// <param name="stream">The stream that contains the audio file data</param>
/// <param name="fileExt">The sound file extension including the period ('.'). Example: ".mp3"</param>
private void CreateReaderStreamFromStream(Stream stream, string fileExt)
{
if(stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (string.IsNullOrEmpty(fileExt) == false)
{
if (fileExt.StartsWith(".") == false)
{
throw new ArgumentOutOfRangeException("File extension expected to start with a period ('.')");
}
}
else
{
throw new ArgumentNullException(nameof(fileExt));
}

var fileFormat = fileExt.GetFormatFromFileExt();

if (fileFormat == AudioFileFormatEnum.WAV)
{
readerStream = new WaveFileReader(stream);
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
{
readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
readerStream = new BlockAlignReductionStream(readerStream);
}
}
else if (fileFormat == AudioFileFormatEnum.MP3)
{
if (Environment.OSVersion.Version.Major < 6)
readerStream = new Mp3FileReader(stream);
else // make MediaFoundationReader the default for MP3 going forwards
readerStream = new StreamMediaFoundationReader(stream);
}
else if (fileFormat == AudioFileFormatEnum.AIFF)
{
readerStream = new AiffFileReader(stream);
}
else
{
// fall back to media foundation reader, see if that can play it
readerStream = new StreamMediaFoundationReader(stream);
}
}

/// <summary>
/// File Name
/// File Name. Value is null when a stream is used to construct the reader
/// </summary>
public string FileName { get; }

Expand Down
128 changes: 128 additions & 0 deletions NAudioTests/WaveStreams/AudioFileExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using NAudio;
using NUnit.Framework;

namespace NAudioTests.WaveStreams
{
public class AudioFileExtensionsTests
{
[Test]
public void LowerCaseWave()
{
var enumVal = ".wav".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.WAV);
}

[Test]
public void MixedCaseWave()
{
var enumVal = ".Wav".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.WAV);
}

[Test]
public void UpperCaseWave()
{
var enumVal = ".WAV".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.WAV);
}

[Test]
public void LowerCaseMp3()
{
var enumVal = ".mp3".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.MP3);
}

[Test]
public void MixedCaseMp3()
{
var enumVal = ".Mp3".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.MP3);
}

[Test]
public void UpperCaseMp3()
{
var enumVal = ".MP3".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.MP3);
}

[Test]
public void LowerCaseAiff()
{
var enumVal = ".aiff".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void MixedCaseAiff()
{
var enumVal = ".Aiff".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void UpperCaseAiff()
{
var enumVal = ".AIFF".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void LowerCaseShortAiff()
{
var enumVal = ".aif".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void MixedCaseShortAiff()
{
var enumVal = ".Aif".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void UpperCaseShortAiff()
{
var enumVal = ".AIF".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.AIFF);
}

[Test]
public void NullValue()
{
var enumVal = ((string)null).GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.Unknown);
}

[Test]
public void EmptyValue()
{
var enumVal = "".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.Unknown);
}

[Test]
public void UnknownValue()
{
var enumVal = ".abc".GetFormatFromFileExt();

Assert.AreEqual(enumVal, AudioFileFormatEnum.Unknown);
}
}
}
Loading