/// <summary>
 /// 矩形波をFFTにより周波数分析を行う
 /// <para>実行後、ファイルが作成されますのでソースコードと合わせて確認してみてください。</para>
 /// </summary>
 public static void FftSquare()
 {
     double freq = 50.0;
     double samplingFreq = freq * 100.0;
     var dft = new DFT(FFTpoint.size4096, Window.WindowKind.NoWindow);
     var result = dft.FFT(SignalTest.GetSquare(freq, 0.5, samplingFreq, 1.0, dft.FFTsize), samplingFreq);
     result.Save("FftSquare.csv");
     return;
 }
        // ---- メソッド ---------------------------------------
        /// <summary>
        /// 特徴ベクトルを返す
        /// </summary>
        /// <returns>特徴ベクトル</returns>
        public override PatternRecognition.Feature GetFeature()
        {
            if (this.Ready)
            {
                // 最大のパワーとなった帯域を探すとその最大値を求める
                double maxPSDofBand = this._sumOfPSDbyBand.Max();                                           // 最大値を取得
                int indexOfMax = -1;                                                                        // -1としておくことで、↓2行のアルゴリズムにより最大となる帯域のインデックス-1を得る
                for (int i = 0; i < this._sumOfPSDbyBand.Length && this._sumOfPSDbyBand[i] != maxPSDofBand; i++) indexOfMax = i;// 最大となった帯域のインデックス-1を取得する
                indexOfMax++;                                                                               // 等しいと代入の前にループを抜けるので、抜けた後で加算してインデックスとする
                // SN比を求める。求まればよいが。
                double noiseLevelPSD = this._timeSeriesPSD[0][indexOfMax];                                  // フレームの初めはノイズレベルに近いはず
                double maxPSDofFlame = double.MinValue;
                for (int i = 0; i < this._timeSeriesPSD.Count; i++) if (maxPSDofFlame < this._timeSeriesPSD[i][indexOfMax]) maxPSDofFlame = this._timeSeriesPSD[i][indexOfMax];
                this._SNratio = 10.0 * Math.Log10(maxPSDofFlame / noiseLevelPSD);
                // 帯域パワーの最大値が1になるように正規化
                var fea1 = new PatternRecognition.Feature(this._sumOfPSDbyBand);
                fea1.Normalizing();

                // 変調スペクトルをもとめる
                double[] modulatedSongAtUniBand = new double[this._timeSeriesPSD.Count];
                for (int i = 0; i < modulatedSongAtUniBand.Length; i++) modulatedSongAtUniBand[i] = this._timeSeriesPSD[i][indexOfMax];// 最大であった帯域のパワーを取得する(変調されたパワー)
                DFT fft = new DFT(modulatedSongAtUniBand.Length, Window.WindowKind.Hanning);                // FFTを実施する準備
                FFTresult modulationSpectrum = fft.FFT(modulatedSongAtUniBand, this._condition.FrequencyOfFFT);
                double[] spectrum = new double[this._condition.MaxModulationSpectrumFrequency * 10];
                //for (int i = 0; i < spectrum.Length; i++)
                Parallel.For(0, spectrum.Length, i =>
                {
                    double minFrequency = (double)i * 0.1;
                    double maxFrequency = minFrequency + 0.1;
                    spectrum[i] = modulationSpectrum.GetPSD(minFrequency, maxFrequency);                    // 帯域毎にコピー
                });
                // コピーした変調スペクトルを正規化
                var fea2 = new PatternRecognition.Feature(spectrum);
                fea2.Normalizing();

                // 特徴ベクトルとしてまとめる
                var ans = new PatternRecognition.Feature(fea1.ToArray());
                ans.Add(fea2.ToArray());
                ans.Normalizing();

                return ans;
            }
            else
                return null;
        }
        /// <summary>
        /// サイン関数をFFTにより周波数分析を行う
        /// <para>実行後、2つのファイルが作成されますのでソースコードと合わせて確認してみてください。</para>
        /// </summary>
        public static void FftSin()
        {
            double freq, samplingFreq;
            var dft = new DFT(FFTpoint.size4096, Window.WindowKind.NoWindow);

            // part 1
            // デフォルトではFFT窓の幅よりも小さなデータになるようにした場合
            freq = 60.5;
            samplingFreq = freq * 4.0;
            var result = dft.FFT(SignalTest.GetSin(freq, 0.0, samplingFreq, 1.0, dft.FFTsize / 2), samplingFreq);
            result.Save("FftSin part 1.csv");

            // part 2
            // 窓いっぱいに波形を埋めた場合
            freq = 60.5;
            samplingFreq = freq * 4.0;
            result = dft.FFT(SignalTest.GetSin(freq, 0.0, samplingFreq, 1.0, dft.FFTsize), samplingFreq);
            result.Save("FftSin part 2.csv");
        }
        /// <summary>
        /// 音声ファイルを開き、生成された特徴ベクトルのリストを返す
        /// </summary>
        /// <param name="waveFilePath">WAVEファイルのフルパス</param>
        /// <param name="discriminator">識別器<para>識別させない場合は省略して下さい。</para></param>
        /// <param name="threshold">棄却レベル<para>識別のさせる場合、この値を下回ると識別結果をUnknownと判定します。</para></param>
        /// <returns>
        /// 生成された特徴ベクトルのリスト
        /// <para>識別器を指定している場合は、識別結果を添付します。</para>
        /// </returns>
        public List<Result> GenerateFeatureList(string waveFilePath, Discriminator discriminator = null, double threshold = 0.0)
        {
            var ans = new List<Result>(0);                                                          // 結果を格納する

            if (System.IO.File.Exists(waveFilePath))
            {
                WaveReader wave_file = new WaveReader();
                wave_file.Open(waveFilePath);                                                       // waveファイルを開く
                if (wave_file.IsOpen)
                {
                    try
                    {
                        this.Lock = true;
                        this.SetGenerator(wave_file);
                        this._generator.Log = this.Log;
                        DFT fft = new DFT(this.ReadAmount, Window.WindowKind.Hanning);              // FFTを実施する準備
                        while (wave_file.IsOpen && wave_file.ReadLimit == false)                    // 読めるなら、全て読むまでループ
                        {
                            MusicData data = wave_file.Read(this.ReadAmount);                       // ファイルからread_size_sec秒間読み込み。
                            double[] sound;
                            if (data.UsableCH == MusicData.UsableChannel.Left)                      // データの入っている方を格納する
                                sound = data.GetData(MusicData.Channel.Left);                       //
                            else
                                sound = data.GetData(MusicData.Channel.Right);
                            FFTresult result = fft.FFT(sound, (double)wave_file.SamplingRate);      // FFTを実行. FFTサイズが音声データよりも小さい場合、始めの方しか処理されないので注意.
                            this._generator.Add(result);
                            if (this._generator.Ready)
                            {
                                //Console.WriteLine("検出したようです。");                          // デバッグモードで動かしながら確認するためのコンソール出力
                                Result feature = this._generator.GetFeature();                      // 特徴ベクトル生成
                                if (discriminator == null)
                                    ans.Add(feature);                                               // 答えを格納
                                else
                                {
                                    Discriminator.IDandLikelihood id = discriminator.Discriminate(feature.FeatureVector);
                                    string name = id.ID;
                                    if (id.Likelihood < threshold) name = "Unknown";
                                    feature.DiscriminationResult = name;
                                    if (!(this.MatchOnlyAtDiscrimination == true && name != feature.FilterName)) ans.Add(feature);
                                }
                            }
                        }
                    }
                    catch (SystemException e)
                    {
                        throw new Exception(e.Message);
                    }
                }
                else
                    throw new Exception("Waveファイルを開くことができませんでした。\nWaveReaderクラスエラーメッセージ:" + wave_file.ErrorMessage);
                wave_file.Close();                                                              // waveファイルを閉じる
            }
            this.Lock = false;
            return ans;
        }