public float[] FrequencyFiltration(int windowLength, int filterLength, double cutFreq, int windowHopSize, int windowType, string filterType)
        {
            // rozmiar transformacji DFT
            int n = SoundUtil.GetExpandedPow2(windowLength + filterLength - 1);

            // d³ugoœæ sygna³u wynikowego
            int size = dataNormalized.Length + n - windowLength;

            float[] result = new float[size];

            // okna
            double[][]  windows        = new double[size / windowHopSize][];
            Complex[][] windowsComplex = new Complex[size / windowHopSize][];

            for (int i = 0; i < windows.Length; i++)
            {
                windows[i]        = new double[n];
                windowsComplex[i] = new Complex[n];
            }

            // wyliczenie wspó³czynników okna
            double[] windowFactors = SoundUtil.Factors(windowType, windowLength);

            for (int i = 0; i < windows.Length; i++)
            {
                // wymno¿enie wspó³czynników okna przez wartoœci sygna³u
                for (int j = 0; j < windowLength; j++)
                {
                    if (i * windowHopSize + j < dataNormalized.Length)
                    {
                        windows[i][j] = windowFactors[j] * dataNormalized[i * windowHopSize + j];
                    }
                    else
                    {
                        windows[i][j] = 0;
                    }
                }

                // uzupe³nienie pozosta³ych miejsc zerami
                for (int j = windowLength; j < n; j++)
                {
                    windows[i][j] = 0;
                }
            }

            // wyliczenie wspó³czynników okna
            double[] windowFilterFactors = SoundUtil.Factors(windowType, filterLength);

            // wyliczenie wspó³czynników filtru
            double[] filterFactors = SoundUtil.LowPassFilterFactors(cutFreq, sampleRate, filterLength);
            double[] filtered      = new double[n];

            // wymno¿enie wspó³czynników okna i filtru
            for (int i = 0; i < filterLength; i++)
            {
                filtered[i] = windowFilterFactors[i] * filterFactors[i];
            }

            // uzupe³nienie pozosta³ych miejsc zerami
            for (int i = filterLength; i < n; i++)
            {
                filtered[i] = 0;
            }

            if (filterType == "notCasual")
            {
                // dla filtra nieprzyczynowego przesuwamy po³owê wartoœci filtra na jego koniec
                int shiftNumberFilter = (filterLength - 1) / 2;
                IEnumerable <double> shiftedFilter = filtered.Take(shiftNumberFilter);
                List <double>        filteredTemp  = filtered.Skip(shiftNumberFilter).ToList();
                filteredTemp.AddRange(shiftedFilter);
                filtered = filteredTemp.ToArray();
            }

            // zmiana przefiltrowanych wspó³czynników na liczby zespolone i wykonanie FFT
            Complex[] complexSound    = SoundUtil.SignalToComplex(filtered);
            Complex[] filteredComplex = SoundUtil.FFT(complexSound);

            for (int i = 0; i < windows.Length; i++)
            {
                // zmiana wartoœci sygna³u okna na liczby zespolone i wykonanie FFT
                windowsComplex[i] = SoundUtil.FFT(SoundUtil.SignalToComplex(windows[i]));

                // wymno¿enie wartoœci sygna³u okna ze wspó³czynnikami
                for (int j = 0; j < windowsComplex[i].Length; j++)
                {
                    windowsComplex[i][j] *= filteredComplex[j];
                }

                // wykonanie IFFT na oknie i zmiana na sygna³ wyjœciowy
                windows[i] = SoundUtil.SignalFromComplex(SoundUtil.IFFT(windowsComplex[i]));
            }

            // dodanie wyniku do sygna³u wyjœciowego
            for (int i = 0; i < windows.Length; i++)
            {
                for (int j = 0; j < windows[i].Length; j++)
                {
                    if (i * windowHopSize + j < dataNormalized.Length)
                    {
                        result[i * windowHopSize + j] += (float)windows[i][j];
                    }
                }
            }

            return(result);
        }
        public List <int> Cepstrum()
        {
            int        chunkSize   = this.chunkSize;
            List <int> frequencies = new List <int>();

            float[][] parts;

            // ustawiamy chunkSize jako potêge 2 dla póŸniejszego FFT
            chunkSize = SoundUtil.MakePowerOf2(chunkSize);
            // dzielimy pobrane próbki dŸwiêku na fragmenty
            parts = SoundUtil.ChunkArrayPowerOf2(dataNormalized, chunkSize);

            foreach (float[] buffer in parts)
            {
                // konwertujemy dane wejœciowe spróbkowanego sygna³u na postaæ zespolon¹
                Complex[] complexSound = SoundUtil.SignalToComplex(buffer);

                // przepuszczamy dane przez okno Hamminga
                Complex[] complexWindows = SoundUtil.HammingWindow(complexSound);

                // pierwsze FFT
                Complex[] fftComplex = SoundUtil.FFT(complexWindows);

                // zapamiêtujemy wynik pierwszego FFT w celu wyœwietlenia wykresu
                fftDataForSpectrumChart = new Complex[fftComplex.Length];
                fftComplex.CopyTo(fftDataForSpectrumChart, 0);

                // przepuszczamy modu³ widma przez logarytm
                for (int i = 0; i < fftComplex.Length; ++i)
                {
                    fftComplex[i] = new Complex(10.0f * (float)Math.Log10(fftComplex[i].Modulus() + 1), 0);
                }

                // drugie FFT, w celu uzyskania cepstrum
                fftComplex = SoundUtil.FFT(fftComplex);

                // zapamiêtujemy wynik cepstrum w celu wyœwietlenia wykresu
                cepstrum = new Complex[fftComplex.Length];
                fftComplex.CopyTo(cepstrum, 0);

                // bierzemy 1/4 wyników bo po ka¿dym FFT po³owa wyników jest taka sama a FFT wykonujemy 2 razy
                fftComplex = fftComplex.Take(fftComplex.Length / 4).ToArray();

                // modu³y wartoœci z FFT
                double[] dataArray = new double[chunkSize];

                // zapisanie modu³ów wartoœci z FFT do pierwszego wymiaru bufora
                for (int i = 0; i < fftComplex.Length; ++i)
                {
                    dataArray[i] = fftComplex[i].Modulus();
                }

                List <int> localMaxIndexes = new List <int>();
                //promieñ, zakres w którym badamy czy wartoœæ jest maksimum lokalnym
                int range = 10;

                for (int i = range; i < dataArray.Length - range; i++)
                {
                    int biggerValue = 0;
                    // badamy otoczenie punktu
                    for (int j = i - range; j < i + range; ++j)
                    {
                        // je¿eli bie¿¹ca wartoœæ z otoczenia jest mniejsza od punktu,
                        // który sprawdzamy wtedy zliczamy je
                        if (dataArray[j] <= dataArray[i] && i != j)
                        {
                            biggerValue++;
                        }
                    }

                    // jeœli w otoczeniu nie ma mniejszych wartoœci to
                    // dodajemy numer wartoœci do tablicy lokalnych maksimów
                    if (biggerValue == (range * 2) - 1)
                    {
                        localMaxIndexes.Add(i);
                    }
                }

                // odrzucanie wysokich wartoœci ale nie stromych
                // musza opadaæ w obu kierunkach
                for (int index = 0; index < localMaxIndexes.Count;)
                {
                    int i = localMaxIndexes[index], leftIndexOffset = 0, rightIndexOffset = 0;

                    // badamy lewe zbocze w poszukiwaniu wartoœci najni¿szej wartoœci,
                    // w której zmienia siê omnotonicznoœæ
                    while ((i - leftIndexOffset - 1) >= 0)
                    {
                        if ((dataArray[i - leftIndexOffset - 1] <= dataArray[i - leftIndexOffset]))
                        {
                            ++leftIndexOffset;
                        }
                        else
                        {
                            break;
                        }
                    }

                    // badamy prawe zbocze w poszukiwaniu najwy¿szej wartoœci
                    // w której zmienia siê omnotonicznoœæ
                    while ((i + rightIndexOffset + 1) < dataArray.Length)
                    {
                        if ((dataArray[i + rightIndexOffset + 1] <= dataArray[i + rightIndexOffset]))
                        {
                            ++rightIndexOffset;
                        }
                        else
                        {
                            break;
                        }
                    }

                    // progowanie co do najwiêkszego peaku
                    // wybieramy punktu o wiêkszej wartoœci spoœród znalezionych po lewej i prawej stronoe
                    double maxmin = Math.Max(dataArray[i - leftIndexOffset], dataArray[i + rightIndexOffset]);

                    // porównujemy wartoœæ maksymaln¹ minimów z wczeœniej znalezionych punktów i porówujemy je z lokalnym maksem
                    // je¿eli najwiêksza wartoœæ minimalna jest wiêksza od bie¿¹co rozpatrywanego poziomu maksimum
                    if (maxmin > dataArray[i] * 0.2)
                    {
                        // wtedy usuwamy to maksimum
                        localMaxIndexes.RemoveAt(index);
                    }
                    else
                    {
                        index++;
                    }
                }

                // szukanie pozycji maksymalnej wartoœci z listy lokalnych maksimów
                int max_ind = SoundUtil.MaxFromLocalMaxList(localMaxIndexes, dataArray);

                // przeszukanie listy maksimów w celu usuniêcia tych znajduj¹cych siê ponad progiem
                for (int index = 0; index < localMaxIndexes.Count;)
                {
                    // je¿eli bie¿¹ca wartoœæ lokalnego maksimum z listy jest ponad progiem znalezionej wartoœci maksymalnej na liœcie
                    if (dataArray[localMaxIndexes[index]] > dataArray[max_ind] * 0.4)
                    {
                        index++;
                    }
                    else
                    {
                        // w przeciwnym wypadku usuwamy maksimum z listy
                        localMaxIndexes.RemoveAt(index);
                    }
                }

                // zmienne maj¹ce zapamiêtaæ dwa indeksy z listy maksimów
                int max_b, max_a;

                // szukanie pozycji maksymalnej wartoœci z listy lokalnych maksimów
                max_b = SoundUtil.MaxFromLocalMaxList(localMaxIndexes, dataArray);

                int a = 0, b = 0;
                while (localMaxIndexes.Count > 1)
                {
                    // usuwamy wczeœniej zapamiêtane maksimum z listy
                    localMaxIndexes.Remove(max_b);

                    // szukanie pozycji maksymalnej wartoœci z listy lokalnych maksimów
                    max_a = SoundUtil.MaxFromLocalMaxList(localMaxIndexes, dataArray);

                    // zapamiêtanie wczeœniej pozycji wczeœniej usuniêtego maksimum
                    // i tego znaleziono po nim
                    a = max_a; b = max_b;

                    // je¿eli indeksy maksimów nie s¹ w kolejnoœci rosn¹æ zamieniamy je
                    if (a > b)
                    {
                        int tmp = a;
                        a = b;
                        b = tmp;
                    }

                    for (int i = 0; i < localMaxIndexes.Count;)
                    {
                        int num = localMaxIndexes[i];

                        // je¿eli indeks bie¿¹cego maksimum z listy jest poza przedzia³em
                        // wyznaczonym przez poprzednie zapisane maksima
                        if (num < a || num > b)
                        {
                            // usuwamy indeks maksimum z listy
                            localMaxIndexes.RemoveAt(i);
                        }
                        else
                        {
                            i++;
                        }
                    }

                    // najstarszey zapamiêtany indeks maksimum zastêpujemy kolejnym maksimmum po nim
                    max_b = max_a;
                }

                // wyliczamy ró¿nicê miêdzy znalezionymi indeksami maksimów
                max_ind = Math.Abs(b - a);

                // je¿eli lokalnym maksimum z listy jest pierwszy indeks i jedyny indeks na liœcie
                if (max_ind == 0 && localMaxIndexes.Count == 1)
                {
                    max_ind = localMaxIndexes[0];
                }

                // czêstotliwoœæ podstawowa wyliczana jest jako iloraz czêstotliwoœci próbkowania
                // i pozycji maksimum (która tak naprawde wyznacza okres)
                int frequency = (int)(sampleRate / (double)max_ind);
                frequencies.Add(frequency);
            }

            return(frequencies);
        }