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); }