NAudio

導入

NuGetからインストールするならば、次のようにコマンドを実行します。

PM> Install-Package NAudio

これを手動で行うならばNAudio - Download: NAudioからdllをダウンロードし、NAudio.dllをプロジェクトの参照に追加します。

対応ドライバ

  • WaveOut
  • DirectSound
  • ASIO (Audio Stream Input/Output)
  • WASAPI (Windows Audio Session API)

対応フォーマット

  • WAV
  • MP3 (ACMまたはDMO codec)
  • AIFF
  • ADPCM
    • G.711 mu-law and a-law
    • G.722
  • Speex (NSpeex codec)
  • SF2 (SoundFont)
  • インストールされている任意のACM codec
NAudio Features - NAudio - Home

※WAV、MP3、AIFF以外はMedia Foundationを利用するため、実際にサポートされるフォーマットはそれに依存します。

ライセンス

ライセンスはMicrosoft Public License (Ms-PL)です。NAudio - License

再生

AudioFileReader reader = new AudioFileReader("sample.wav");

WaveOut waveOut = new WaveOut();
waveOut.Init(reader);
waveOut.Play();

while (waveOut.PlaybackState == PlaybackState.Playing); // 再生の終了を待つ
// 再生の終了を待たずにWaveOutのインスタンスが破棄されると、その時点で再生が停止する
Playing a WAV - NAudio - Documentation

再生の開始

再生は現在のストリームの位置から開始されるため、Play()で最初から再生されるわけではありません。特にストリームの終端で再生が停止している状態では、Play()では再生が開始されません。よって確実に最初から再生するには、先にreader.Position = 0;のように位置を明示します。

再生の再開

Pause()で一時停止させた場合は、Resume()またはPlay()でその位置から再開できます。この方法ではオーディオデバイスの機能が利用されます。

再生の停止

Stop()で停止を指示できます。ただし直ちに停止するわけではなく、PlaybackStoppedイベントの発生までは停止していません。

private void Method()
{
    if (waveOut.PlaybackState != PlaybackState.Stopped)
    {
        EventHandler<StoppedEventArgs> handler = null;
        handler = delegate
        {
            waveOut.PlaybackStopped -= handler;
            Method(); // ここで停止を確認してから、再帰的に呼び出す
        };
        waveOut.PlaybackStopped += handler;
        waveOut.Stop(); // 停止を指示。まだ停止していない
        return;
    }

    // ここでは停止している
}

即座に停止させたいならば、WaveOut.Dispose()でデバイスごと破棄します。

再生位置の制御

たとえば先頭から5.0秒の位置を再生するには、次のようにCurrentTimeプロパティに設定します。

reader.CurrentTime = TimeSpan.FromSeconds(5.0);

これをバイト単位で指定するには、次のようにPositionプロパティに設定します。

reader.Position = (long)(reader.WaveFormat.AverageBytesPerSecond * 5.0);

なおCurrentTimeプロパティはWaveStreamクラスで次のように実装されているため、この2つの処理の実際は同じです。How do I create a seekbar in C#\NAudio Music Player? - Stack Overflow

public virtual TimeSpan CurrentTime
{
    get { return TimeSpan.FromSeconds((double)Position / WaveFormat.AverageBytesPerSecond); }
    set { Position = (long) (value.TotalSeconds * WaveFormat.AverageBytesPerSecond); }
}

ストリームから読み込むとそのストリームでの位置を示すPositionプロパティの値が変化するため、くり返し読み込むときにはPosition = 0のように位置を戻します。

再生位置の取得

現在の再生位置は、それを設定するのと同様にCurrentTimeプロパティから取得できます。

再生中の位置はWaveOutのGetPosition()メソッドからも取得できますが、こちらは内部的にはwaveOutGetPosition()でオーディオデバイスから取得するため、ストリームの位置から算出するCurrentTimeとは異なる値を返します。waveOutGetPosition function (Windows) | MSDN

AudioFileReader reader = new AudioFileReader("sample.wav");

WaveOut waveOut = new WaveOut();
waveOut.Init(reader);
waveOut.Play();

while (waveOut.PlaybackState == PlaybackState.Playing)
{
    double currentSec1 = reader.CurrentTime.TotalSeconds;
    double currentSec2 = (double)waveOut.GetPosition() / reader.WaveFormat.AverageBytesPerSecond;

    Task.Delay(100).Wait();
}

再生範囲の制御

OffsetSampleProviderクラスで省略時間と再生時間を指定することで、再生範囲を制御できます。How to play a specific portion of an audio file with NAudio

AudioFileReader reader = new AudioFileReader("sample.wav");

OffsetSampleProvider offsetSample = new OffsetSampleProvider(reader);
offsetSample.SkipOver = TimeSpan.FromSeconds(5.0); // 再生前に5.0秒スキップする
offsetSample.Take = TimeSpan.FromSeconds(3.0);     // 3.0秒分 再生する

WaveOut waveOut = new WaveOut();
waveOut.Init(offsetSample);
waveOut.Play();

while (waveOut.PlaybackState == PlaybackState.Playing);
プロパティ 内容
DelayBy 再生前に挿入する、無音の秒数
DelayBySamples 再生前に挿入する、無音のサンプル数
LeadOut 再生に挿入する、無音の秒数
LeadOutSamples 再生に挿入する、無音のサンプル数
SkipOver 再生前に飛び越す秒数
SkipOverSamples 再生前に飛び越すサンプル数
Take 再生する時間 (TimeSpan.Zeroならば、最後まで再生)
TakeSamples 再生するサンプル数 (0ならば、すべて再生)
WaveFormat このSampleProviderのWaveFormat

ファイルからの読み込み

ストリームから読み込むとそのストリームでの位置を示すPositionプロパティの値が変化するため、くり返し読み込むときにはPosition = 0のように位置を戻します。

AudioFileReader

オーディオ ファイルの読み込みを容易にするためのクラスです。

public class AudioFileReader : NAudio.Wave.WaveStream
NAudio/AudioFileReader.cs at master · naudio/NAudio · GitHub
コンストラクタ
AudioFileReader(string)

ファイル名の拡張子によって、異なるストリーム (WaveStream) が生成されます。

フォーマットの判定は拡張子によって行われるため、誤った拡張子が付けられていると例外が発生します。

  • WAVではないファイルに.wavの拡張子 … System.FormatException 「Not a WAVE file - no RIFF header」
  • MP3ではないファイルに.mp3の拡張子 … System.IO.InvalidDataException 「Invalid MP3 file - no MP3 Frames Detected」
  • AIFFではないファイルに.aiffの拡張子 … System.FormatException 「Not an AIFF file - no FORM header.」
メソッド
戻り値の型 メソッド 機能 データ単位 値の範囲
int Read(byte[], int, int) Reads from this wave stream バイト単位 0~255
int Read(float[], int, int) Reads audio from this sample provider サンプリング単位 -1.0~1.0
AudioFileReader reader = new AudioFileReader("sample.wav");

if (reader.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat) // つねにIeeeFloat?
{
    float[] samples = new float[reader.Length / reader.BlockAlign * reader.WaveFormat.Channels];
    reader.Read(samples, 0, samples.Length);

    // ステレオの音声ならば、偶数番目が左のデータで奇数番目が右となる
}

読み込み時にはすべてのデータを読み込む必要があります。さもなくばSystem.ArgumentException例外が発生し、「Must read complete blocks」と通知されます。

  • byte配列での読み込み時 … reader.Length
  • float配列での読み込み時 … reader.Length / (reader.BlockAlign * reader.WaveFormat.Channels)
プロパティ
プロパティ 内容
TimeSpan TotalTime 実時間での合計時間
long Length バイト単位のストリームの長さ
秒単位の長さは、AudioFileReader.Length / AudioFileReader.WaveFormat.AverageBytesPerSecondで得られる
long Position バイト単位のストリームの現在の位置
float Volume Gets or Sets the Volume of this AudioFileReader. 1.0f is full volume
NAudio.Wave.WaveFormat WaveFormat WaveFormat of this stream

サンプル数は、AudioFileReader.Length / AudioFileReader.BlockAlignで求まります。

WaveFileReader

WAVファイルの読み込みを支援するクラスであり、WAVファイルの生のデータを処理できます。

public class WaveFileReader : NAudio.Wave.WaveStream
NAudio/WaveFileReader.cs at master · naudio/NAudio · GitHub
コンストラクタ
WaveFileReader(string)
WaveFileReader(System.IO.Stream)
メソッド
戻り値の型 メソッド 機能
byte[] GetChunkData(NAudio.Wave.RiffChunk) 指定チャンクのデータを取得する
int Read(byte[], int, int) Reads bytes from the Wave File
float[] ReadNextSampleFrame() Attempts to read the next sample or group of samples as floating point normalised into the range -1.0f to 1.0f

モノラルならば1つの要素、ステレオならばインデックス0に左チャンネル、インデックス1に右チャンネルのデータが格納されて返される

bool TryReadFloat(out float) Attempts to read a sample into a float. n.b. only applicable for uncompressed formats. Will normalise the value read into the range -1.0f to 1.0f if it comes from a PCM encoding
プロパティ
  内容
List<NAudio.Wave.RiffChunk> ExtraChunks 追加されたチャンクのリスト
long Length Waveファイルに含まれているオーディオデータのバイト数 (これはdataチャンクの長さであって、Waveファイル自体の長さではない)
long Position Position in the WAV data chunk.
long SampleCount Number of Samples (if possible to calculate)
(This currently does not take into account number of channels, so divide again by number of channels if you want the number of audio 'frames')
NAudio.Wave.WaveFormat WaveFormat  

波形データの取得

WaveFormat.BitsPerSampleが16以上ならば、ReadNextSampleFrame()でサンプルごとに取得できます。

WaveFileReader reader = new WaveFileReader("sample.wav");
// reader.Position = 0; // このreaderをくり返し使用するならば、読み込み前にPositionを0に戻す

float[] samplesL = new float[reader.Length / reader.BlockAlign];
float[] samplesR = new float[reader.Length / reader.BlockAlign];

for (int i = 0; i < samplesL.Length; i++)
{
    float[] sample = reader.ReadNextSampleFrame();

    samplesL[i] = sample[0];
    samplesR[i] = sample[1]; // モノラルでは、このデータは存在しない
}
Read()からの取得

Read()で取得したbyte配列をfloatへ変換するには、次のようにします。

WaveFileReader reader = new WaveFileReader("sample.wav");

byte[] buffer = new byte[reader.Length];
int bytesRead = reader.Read(buffer, 0, buffer.Length);

float[] samplesL = new float[bytesRead / reader.BlockAlign];
float[] samplesR = new float[bytesRead / reader.BlockAlign];

switch (reader.WaveFormat.BitsPerSample)
{
    case 8: // 量子化ビット数が8
        for (int i = 0; i < samplesL.Length; i++)
        {
            samplesL[i] = (buffer[i * reader.BlockAlign] - 128) / 128f;
            samplesR[i] = (buffer[i * reader.BlockAlign + reader.BlockAlign / 2] - 128) / 128f;
        }
        break;

    case 16: // 量子化ビット数が16
        for (int i = 0; i < samplesL.Length; i++)
        {
            samplesL[i] = BitConverter.ToInt16(buffer, i * reader.BlockAlign) / 32768f;
            samplesR[i] = BitConverter.ToInt16(buffer, i * reader.BlockAlign + reader.BlockAlign / 2) / 32768f;
        }
        break;

    case 32: // 量子化ビット数が32
        for (int i = 0; i < samplesL.Length; i++)
        {
            samplesL[i] = BitConverter.ToSingle(buffer, i * reader.BlockAlign);
            samplesR[i] = BitConverter.ToSingle(buffer, i * reader.BlockAlign + reader.BlockAlign / 2);
        }
        break;
}

拡張チャンク

Waveの拡張チャンクの情報は、ExtraChunksプロパティとGetChunkData()メソッドから取得できます。これを利用して、たとえば'id3 'タグの部分をバイトデータで取得するには次のようにします。

WaveFileReader reader = new WaveFileReader(fileName);

List<RiffChunk> extraChunks = reader.ExtraChunks;

RiffChunk riffChunk = extraChunks.Find(
    x => (x.IdentifierAsString.Equals("id3 ", StringComparison.OrdinalIgnoreCase));

byte[] chunkData = reader.GetChunkData(riffChunk);

Mp3FileReader

MP3ファイルから読み込むためのクラスです。

public class Mp3FileReader : NAudio.Wave.WaveStream
NAudio/Mp3FileReader.cs at master · naudio/NAudio · GitHub
コンストラクタ
Mp3FileReader(string)
Mp3FileReader(string, FrameDecompressorBuilder)
Mp3FileReader(Stream)
Mp3FileReader(Stream, FrameDecompressorBuilder)

引数を文字列で与えファイル名から生成する形式では、内部ではFile.OpenRead()によりストリームの形式のコンストラクタが呼ばれます。

このクラスのオブジェクトを生成すると、List<Mp3Index>のtableOfContentsで多量のオブジェクトが生成され、メモリが大量に消費されます。

メソッド
戻り値の型 メソッド 機能
int Read(byte[], int, int) Reads decompressed PCM data from our MP3 file.
NAudio.Wave.Mp3Frame ReadNextFrame() Reads the next mp3 frame.
プロパティ
プロパティ 内容
byte[] Id3v1Tag ID3v1 tag
NAudio.Wave.Id3v2Tag Id3v2Tag ID3v2 tag

タグ

Id3v1Tag、Id3v2Tagプロパティから、それぞれの規格のタグを読み取れます。Create an ID3v2 Tag - NAudio - Home

ID3v1からの読み取り
Mp3FileReader reader = new Mp3FileReader(fileName);
byte[] v1 = reader.Id3v1Tag;

Encoding encoding = Encoding.Default;
char[] trimChars = { '\0' };


string title = encoding.GetString(v1, 3, 30).TrimEnd(trimChars);
string artist = encoding.GetString(v1, 33, 30).TrimEnd(trimChars);
string album = encoding.GetString(v1, 63, 30).TrimEnd(trimChars);
string year = encoding.GetString(v1, 93, 4).TrimEnd(trimChars);
ID3v2.3からの読み取り

ファイルのヘッダ部に"ID3"の文字がない場合には、Id3v2Tagプロパティからはnullが返されます。

Mp3FileReader reader = new Mp3FileReader(fileName);
byte[] v2 = reader.Id3v2Tag.RawData;

// タグはID3v2.3であると仮定し、ヘッダを無視

Dictionary<string, string> tags = new Dictionary<string, string>();
for (int index = 10; index < v2.Length;)
{
    if (v2[index] == '\0') break;

    string frameID = Encoding.ASCII.GetString(v2, index, 4);
    index += 4;

    if (BitConverter.IsLittleEndian)
    {
        Array.Reverse(v2, index, 4);
    }
    int frameSize = BitConverter.ToInt32(v2, index);
    index += 4;

    byte flag1 = v2[index++];
    byte flag2 = v2[index++];

    if (frameID[0] == 'T') // テキスト情報フレームならば読み込む
    {
        Encoding encoding = Encoding.Unicode; // UTF-16LE
        switch (v2[index++])
        {
            case 0x00:
                encoding = Encoding.GetEncoding(28591); // iso-8859-1
                break;

            case 0x01:
                if (index + 1 < v2.Length && (v2[index] == 0xfe && v2[index + 1] == 0xff))
                {
                    encoding = Encoding.BigEndianUnicode; // UTF-16BE
                }
                break;
        }

        string content = encoding.GetString(v2, index, frameSize - 1);
        index += frameSize - 1;

        tags.Add(frameID, content);
    }
    else
    {
        index += frameSize;
    }
}


string title = tags["TIT2"];
string artist = tags["TPE1"];
string album = tags["TALB"];

MediaFoundationReader

任意のファイルを読み込むためのクラスです。

public class MediaFoundationReader : NAudio.Wave.WaveStream
NAudio/MediaFoundationReader.cs at master · naudio/NAudio · GitHub

ファイルを開くときはMFCreateSourceReaderFromURL()が呼ばれるため、サポートされるファイル形式はこれに依存します。

コンストラクタ

public MediaFoundationReader(string file)

ファイルパスや、http://、mms://、file://のURLでも指定できます。

対応しないファイルを指定すると、

  • 「指定された URL のバイト ストリーム タイプはサポートされていません。(0xC00D36C4)」
  • 「コンテンツをエンコードまたはデコードするための適切な変換が見つかりませんでした。 (0xC00D5212)」

として、COMException例外が発生します。

ファイルへの書き込み

書き込み時にはフォーマットを指定する必要があるためAudioFileWriterのような汎用のクラスはなく、それぞれ専用のクラスを用います。

WaveFileWriter

public class WaveFileWriter : System.IO.Stream
NAudio/WaveFileWriter.cs at master · naudio/NAudio · GitHub

書き込まれるファイルはWaveFormat.Serialize()の処理により、拡張チャンクがなくてもfmtチャンクのバイト数は18となります。

コンストラクタ
WaveFileWriter(string, NAudio.Wave.WaveFormat)
WaveFileWriter(System.IO.Stream, NAudio.Wave.WaveFormat)
メソッド
戻り値の型 メソッド 機能
void CreateWaveFile(string, NAudio.Wave.IWaveProvider) WaveProviderからすべてのデータを読み込み、ファイルに書き込む
void CreateWaveFile16(string, NAudio.Wave.ISampleProvider)  
void Flush()  
int Read(byte[], int, int)  
long Seek(long, System.IO.SeekOrigin)  
void SetLength(long)  
void Write(byte[], int, int) 指定データをファイルに追記する
void WriteData(byte[], int, int) (非推奨) 指定データをファイルに追記する
void WriteData(short[], int, int) (非推奨)
void WriteSample(float) 1つのサンプルを、ファイルに書き込む
void WriteSamples(float[], int, int) 32ビットの浮動小数点数のサンプルを、ファイルに書き込む
void WriteSamples(short[], int, int) 16ビットのサンプルを、ファイルに書き込む
プロパティ
プロパティ 内容
bool CanRead  
bool CanSeek  
bool CanWrite  
string Filename  
long Length dataチャンクのオーディオ部のバイト数
long Position Waveファイル内の位置 (これまでに書き込んだバイト数)
NAudio.Wave.WaveFormat WaveFormat  
void WriteSamples(
    float[] samples,
    int offset,
    int count
)

指定範囲の切り出し

AudioFileReader reader = new AudioFileReader("original.mp3");

int startSec = 5;
int endSec = 10;

int startByte = startSec * reader.WaveFormat.AverageBytesPerSecond;
int endByte = endSec * reader.WaveFormat.AverageBytesPerSecond;

byte[] buffer = new byte[endByte - startByte];

reader.Position = startByte;
int bytesRead = reader.Read(buffer, 0, buffer.Length);

using (WaveFileWriter writer = new WaveFileWriter("target.wav", reader.WaveFormat))
{
    writer.Write(buffer, 0, bytesRead);
}
Sound Code: Trimming a WAV file using NAudio

フォーマット

WaveFormat

public class WaveFormat
NAudio/WaveFormat.cs at master · naudio/NAudio · GitHub
プロパティ
プロパティ 内容
int AverageBytesPerSecond 1秒あたりの平均バイト数を返す (SampleRate * BlockAlignに等しい)
int BitsPerSample サンプルあたりのビット数を返す。量子化ビット数 (たいてい16か32、ときに24か8)
int BlockAlign ブロック アラインメントを返す (データの最小単位)
int Channels チャンネル数を返す (1=モノラル、2=ステレオなど)
NAudio.Wave.WaveFormatEncoding Encoding Returns the encoding type used
int ExtraSize Returns the number of extra bytes used by this waveformat. Often 0, except for compressed formats which store extra data after the WAVEFORMATEX header
int SampleRate サンプリング周波数 (サンプリングレート) を返す (1秒あたりのサンプル数)
メソッドごとの、生成される形式の比較
メソッド WaveFormatEncoding SampleRate BitsPerSample Channels
WaveFormat() Pcm 44100 16 2
WaveFormat(int rate, int channels) Pcm rate 16 channels
WaveFormat(int rate, int bits, int channels) Pcm rate bits channels
CreateALawFormat(int rate, int channels) ALaw rate 8 channels
CreateMuLawFormat(int rate, int channels) MuLaw rate 8 channels
CreateIeeeFloatWaveFormat(int rate, int channels) IeeeFloat rate 32 channels
ratebitschannelsとなっている項目は引数の値。それ以外は、表記されている既定値が適用される。

フォーマットの変換

WaveFormatConversionStreamでフォーマットを変換できます。

WaveFormat format = new WaveFormat(16000, 16, 1);
WaveFileReader reader = new WaveFileReader("sample1.wav");

using (WaveFormatConversionStream stream = new WaveFormatConversionStream(format, reader))
{
    WaveFileWriter.CreateWaveFile("sample2.wav", stream);
}

MP3から変換する場合も同様です。Converting MP3 to WAV with NAudio

WaveFormat format = new WaveFormat(16000, 16, 1);
Mp3FileReader reader = new Mp3FileReader("sample1.mp3");

using (WaveFormatConversionStream stream = new WaveFormatConversionStream(format, reader))
{
    WaveFileWriter.CreateWaveFile("sample2.wav", stream);
}

変換結果を直接利用したいならば、ファイルではなくbyte配列に書き出します。

WaveFormat format = new WaveFormat(16000, 16, 1);
WaveFileReader reader = new WaveFileReader("sample1.wav");

using (WaveFormatConversionStream stream = new WaveFormatConversionStream(format, reader))
{
    byte[] buffer = new byte[stream.Length];
    stream.Read(buffer, 0, buffer.Length);
}

ただしこのWaveFormatConversionStreamでフォーマットを変換すると、音質が劣化することがあります。その場合にはMediaFoundationEncoderを用います。

MediaFoundationResampler

指定のファイル形式へ、指定のフォーマットで出力できます。

public class MediaFoundationResampler : NAudio.MediaFoundation.MediaFoundationTransform
NAudio/MediaFoundationResampler.cs at master · naudio/NAudio · GitHub
WaveFormat format = new WaveFormat(16000, 16, 1);

using (MediaFoundationReader reader = new MediaFoundationReader("sample.mp3"))
using (MediaFoundationResampler resampler = new MediaFoundationResampler(reader, format))
{
    WaveFileWriter.CreateWaveFile("sample.wav", resampler);

    // このreaderから再び読み取るならば、ストリームの位置を戻す
    // reader.Position = 0;
}

変換結果を直接利用するならば、Read()でbyte配列に書き出します。それをサンプル単位で処理するには、さらに必要な形式に変換します。

WaveFormat format = new WaveFormat(16000, 16, 1);

byte[] buffer;
int bytesRead;

using (MediaFoundationReader reader = new MediaFoundationReader("sample.mp3"))
using (MediaFoundationResampler resampler = new MediaFoundationResampler(reader, format))
{
    double c1 = (double)resampler.WaveFormat.SampleRate / reader.WaveFormat.SampleRate;
    double c2 = (double)resampler.WaveFormat.BitsPerSample / reader.WaveFormat.BitsPerSample;
    double c3 = (double)resampler.WaveFormat.Channels / reader.WaveFormat.Channels;

    buffer = new byte[(int)(reader.Length * (c1 * c2 * c3))];
    bytesRead = resampler.Read(buffer, 0, buffer.Length);
}

// float[]へ変換
float[] samples = new float[bytesRead / format.BlockAlign];
if (format.BitsPerSample == 16)
{
    for (int i = 0; i < samples.Length; i++)
    {
        samples[i] = BitConverter.ToInt16(buffer, i * 2) / 32768f;
    }
}
else
{
    throw new InvalidOperationException(String.Format("Unsupported bits per sample: {0}", format.BitsPerSample));
}

// 必要な範囲を処理
using (WaveFileWriter writer = new WaveFileWriter("sample.wav", format))
{
    writer.WriteSamples(samples, 0, samples.Length);
}

MediaFoundationEncoder

Sink Writerでエンコードし、フォーマット変換時の音質劣化を抑えられます。

public class MediaFoundationEncoder : IDisposable
NAudio/MediaFoundationEncoder.cs at master · naudio/NAudio · GitHub

Encode()メソッドで、任意のフォーマットへ変換できます。

public void Encode(
    string outputFile,
    NAudio.Wave.IWaveProvider inputProvider
)
MP3からWAVへの変換
MediaFoundationReader reader = new MediaFoundationReader("sample.mp3");

//
WaveFormat format = new WaveFormat(16000, 16, 1);
MediaFoundation.MediaType mediaType = new MediaFoundation.MediaType(format);

using (MediaFoundationEncoder encoder = new MediaFoundationEncoder(mediaType))
{
    encoder.Encode("sample.wav", reader);
}
WAVからMP3への変換
MediaFoundationReader reader = new MediaFoundationReader("sample.wav");

// MediaTypeにMFAudioFormat_MP3を指定
NAudio.MediaFoundation.MediaType mediaType = MediaFoundationEncoder.SelectMediaType(
    NAudio.MediaFoundation.AudioSubtypes.MFAudioFormat_MP3,
    reader.WaveFormat,
    reader.WaveFormat.BitsPerSample);

using (MediaFoundationEncoder encoder = new MediaFoundationEncoder(mediaType))
{
    encoder.Encode("sample.mp3", reader);
}

MP3などへのフォーマットの変換は、それを簡単に記述できるメソッドが用意されています。

Encode()のヘルパーメソッド
メソッド エンコーディング 対応環境
EncodeToAac(NAudio.Wave.IWaveProvider, string, int) AAC 既定ではWindows 7以降
EncodeToMp3(NAudio.Wave.IWaveProvider, string, int) MP3 既定ではWindows 8以降
EncodeToWma(NAudio.Wave.IWaveProvider, string, int) WMA (Window Media Audio) おそらくWindows Vista以降

ファイル形式の変換

MP3からWAVへの変換

読み込んだMP3のインスタンスをCreateWaveFile()へ渡すことで、WAVとして書き込めます。Convert an MP3 to WAV - NAudio - Documentation

AudioFileReader reader = new AudioFileReader("sample.mp3");
WaveFileWriter.CreateWaveFile("sample.wav", reader);

その他

周波数解析

次のクラスが参考になります。

  • FFT … NAudio.Dsp.FastFourierTransformクラス
  • 周波数スペクトラムの描画 … NAudioWpfDemo.SpectrumAnalyserクラス
c# - Spectrum Analyzer with NAudio & WPFSoundVisualizationLib - Stack Overflow
AudioFileReader reader = new AudioFileReader(fileName);

float[] samples = new float[reader.Length / reader.BlockAlign * reader.WaveFormat.Channels];
reader.Read(samples, 0, samples.Length);


int fftLength = 256;
int fftPos = 0;

float[,] result = new float[samples.Length / fftLength, fftLength / 2];

Complex[] buffer = new Complex[fftLength];
for (int i = 0; i < samples.Length; i++)
{
    buffer[fftPos].X = (float)(samples[i] * FastFourierTransform.HammingWindow(fftPos, fftLength));
    buffer[fftPos].Y = 0.0f;
    fftPos++;

    if (fftLength <= fftPos)
    {
        fftPos = 0;

        int m = (int)Math.Log(fftLength, 2.0);
        FastFourierTransform.FFT(true, m, buffer);

        for (int k = 0; k < result.GetLength(1); k++)
        {
            double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y);
            double intensityDB = 10.0 * Math.Log10(diagonal);

            const double minDB = -60.0;
            double percent = (intensityDB < minDB) ? 1.0 : intensityDB / minDB;

            result[i / fftLength, k] = (float)percent;
        }
    }
}

音圧調整

Microsoft Learnから検索