NAudio | STFT - peace098beat/windows_applicaciton GitHub Wiki

using NAudio.Dsp;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace FiNAudio0
{

    public enum SelectedChannel
    {
        Left,
        Right,
        Mono
    }

    public partial class Form1 : Form
    {

        public string CurrentFilePath = "";
        public WaveFileReader reader;

        public Form1()
        {
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);



        }

        private void button_LoadWav_Click(object sender, EventArgs e)
        {
            /// file load
            CurrentFilePath = Path.GetFullPath(@"audio\\TestAudio-44100-16-stereo-10m.wav");
            Debug.Assert(File.Exists(CurrentFilePath));

            if (File.Exists(CurrentFilePath))
            {
            }
            else
            {
                MessageBox.Show(CurrentFilePath,
                                "エラー",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }


            /// file load
            CurrentFilePath = Path.GetFullPath(@"audio\\TestAudio-44100-16-stereo-10m.wav");
            Debug.Assert(File.Exists(CurrentFilePath));

            /// NAudio
            this.reader = new NAudio.Wave.WaveFileReader(CurrentFilePath);
            TimeSpan totalTime = this.reader.TotalTime;
            int fs = this.reader.WaveFormat.SampleRate;
            int bits = this.reader.WaveFormat.BitsPerSample;
            int channels = this.reader.WaveFormat.Channels;

            this.Text = Path.GetFileName(CurrentFilePath);
            textBox1.Text += Path.GetFileName(CurrentFilePath) + Environment.NewLine + Environment.NewLine;
            textBox1.Text += "Total Time : " + totalTime.ToString() + Environment.NewLine;
            textBox1.Text += "Sample Rate : " + fs.ToString() + Environment.NewLine;
            textBox1.Text += "Bits Per Sampel : " + bits.ToString() + Environment.NewLine;
            textBox1.Text += "Channels : " + channels.ToString() + Environment.NewLine;
            textBox1.Text += "SampleCount : " + this.reader.SampleCount.ToString() + Environment.NewLine;

            // Param
            SelectedChannel selectedChannel = SelectedChannel.Mono;

            //待機状態
            this.Cursor = Cursors.WaitCursor;

            // Current Reader
            //var reader = this.reader;
            //// 16bits Only
            Debug.Assert(reader.WaveFormat.BitsPerSample == 16);


            /*****************************************************************************************
            /* Read Samples
            /*****************************************************************************************/
            //// Create Buffer
            byte[] buffer = new byte[reader.Length];
            int bytesRead = reader.Read(buffer, 0, buffer.Length);

            float[] samples = new float[bytesRead / reader.BlockAlign];

            // read byte
            for (int i = 0; i < samples.Length; i++)
            {
                float samplesL = BitConverter.ToInt16(buffer, i * reader.BlockAlign) / 32768f;
                float samplesR = BitConverter.ToInt16(buffer, i * reader.BlockAlign + reader.BlockAlign / 2) / 32768f;
                switch (selectedChannel)
                {
                    case SelectedChannel.Left:
                        samples[i] = samplesL;
                        break;
                    case SelectedChannel.Right:
                        samples[i] = samplesR;
                        break;
                    case SelectedChannel.Mono:
                        samples[i] = (samplesL + samplesR) / 2F;
                        break;
                    default:
                        break;
                }
            }


            /*****************************************************************************************
            /* STFT (no overlap)
            /*****************************************************************************************/
            // Parameters
            int fftLength = 8096;
            int fftPos = 0;
            float[,] result = new float[samples.Length / fftLength, fftLength / 2];

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

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

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

                    for (int k = 0; k < result.GetLength(1); k++)
                    {
                        double thr = 1e-10;
                        double power = Math.Sqrt(cBuffer[k].X * cBuffer[k].X + cBuffer[k].Y * cBuffer[k].Y);
                        double LogPow = 10.0 * Math.Log10(power + thr);

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

                        result[i / fftLength, k] = (float)LogPow;
                    }
                }
            }
            // End FFT


            this.Invalidate();
            this.Update();
            this.Refresh();

            /*****************************************************************************************
            /* Chart 1
            /*****************************************************************************************/
            int CHART_SKIP = 10000;

            //デフォルトで追加されているSeriesとLegendの初期化
            chart1.Series.Clear();
            chart1.Legends.Clear();

            //Seriesの作成
            Series test = new Series();
            test.ChartType = SeriesChartType.Line;
            for (int i = 0; i < samples.Length; i += CHART_SKIP)
            {
                test.Points.AddXY((double)i / fs, samples[i]);
            }
            chart1.Series.Add(test);


            /*****************************************************************************************
            /* Chart 2
            /*****************************************************************************************/
            int CurrentFrame = 1000;

            // 準備
            int MaxFrame = result.GetLength(0);
            int NFreq = result.GetLength(1);
            int NFreqHarf = NFreq / 2;

            //デフォルトで追加されているSeriesとLegendの初期化
            chart2.Series.Clear();
            chart2.Legends.Clear();

            //Seriesの作成
            Series chart2_series = new Series();
            chart2_series.ChartType = SeriesChartType.Line;
            for (int i = 0; i < NFreqHarf; i++)
            {
                double x = (double)i * (double)fs / (double)NFreq;
                double y = result[CurrentFrame, i];

                chart2_series.Points.AddXY(x, y);
            }
            chart2.Series.Add(chart2_series);

            //元に戻す
            this.Cursor = Cursors.Default;
        }
    }
}