NuGetからインストールするならば、次のようにコマンドを実行します。
PM> Install-Package NAudio
これを手動で行うならばNAudio - Download: NAudioからdllをダウンロードし、NAudio.dllをプロジェクトの参照に追加します。
※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
のように位置を戻します。
オーディオ ファイルの読み込みを容易にするためのクラスです。
public class AudioFileReader : NAudio.Wave.WaveStreamNAudio/AudioFileReader.cs at master · naudio/NAudio · GitHub
AudioFileReader(string) |
ファイル名の拡張子によって、異なるストリーム (WaveStream) が生成されます。
フォーマットの判定は拡張子によって行われるため、誤った拡張子が付けられていると例外が発生します。
戻り値の型 | メソッド | 機能 | データ単位 | 値の範囲 |
---|---|---|---|---|
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」と通知されます。
reader.Length
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
で求まります。
WAVファイルの読み込みを支援するクラスであり、WAVファイルの生のデータを処理できます。
public class WaveFileReader : NAudio.Wave.WaveStreamNAudio/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()で取得した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);
MP3ファイルから読み込むためのクラスです。
public class Mp3FileReader : NAudio.Wave.WaveStreamNAudio/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
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);
ファイルのヘッダ部に"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"];
任意のファイルを読み込むためのクラスです。
public class MediaFoundationReader : NAudio.Wave.WaveStreamNAudio/MediaFoundationReader.cs at master · naudio/NAudio · GitHub
ファイルを開くときはMFCreateSourceReaderFromURL()が呼ばれるため、サポートされるファイル形式はこれに依存します。
public MediaFoundationReader(string file)
ファイルパスや、http://、mms://、file://のURLでも指定できます。
対応しないファイルを指定すると、
として、COMException例外が発生します。
書き込み時にはフォーマットを指定する必要があるためAudioFileWriterのような汎用のクラスはなく、それぞれ専用のクラスを用います。
public class WaveFileWriter : System.IO.StreamNAudio/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
public class WaveFormatNAudio/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 |
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を用います。
指定のファイル形式へ、指定のフォーマットで出力できます。
public class MediaFoundationResampler : NAudio.MediaFoundation.MediaFoundationTransformNAudio/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); }
Sink Writerでエンコードし、フォーマット変換時の音質劣化を抑えられます。
public class MediaFoundationEncoder : IDisposableNAudio/MediaFoundationEncoder.cs at master · naudio/NAudio · GitHub
Encode()メソッドで、任意のフォーマットへ変換できます。
public void Encode( string outputFile, NAudio.Wave.IWaveProvider inputProvider )
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); }
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などへのフォーマットの変換は、それを簡単に記述できるメソッドが用意されています。
メソッド | エンコーディング | 対応環境 |
---|---|---|
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のインスタンスをCreateWaveFile()へ渡すことで、WAVとして書き込めます。Convert an MP3 to WAV - NAudio - Documentation
AudioFileReader reader = new AudioFileReader("sample.mp3"); WaveFileWriter.CreateWaveFile("sample.wav", reader);
次のクラスが参考になります。
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; } } }