Внедряване на алгоритъм за хармоничен продуктов спектър в java

В момента работя върху програма за настройка на китара в Java и се опитвам да внедря алгоритъм за хармоничен продуктов спектър, за да определя основната честота.

За момента създадох метод, който намалява моя спектър с фактор f.

Сега се опитвам да умножа всички мои различни спектри с намалена дискретизация заедно. Аз кодирам в java и работя с масиви. Следователно имам масиви за индексите, които са били намалени, и масиви за различните стойности на моя спектър. В момента се опитвам да сложа всички различни масиви с еднакъв размер и да организирам стойностите им, така че да съответстват на правилния индекс с намалена дискретизация. Имам много проблеми с размера и стойностите....

Освен това се опитвам да приложа този алгоритъм въз основа на пример, който имам на лист хартия... Следователно мога да приложа този алгоритъм само с 4 намалени спектъра, но се съмнявам, че това ще е достатъчно, когато всъщност ще използвам истински звуков сигнал .

Ето кода за метода за понижаване на пробите:

import org.jfree.ui.RefineryUtilities;

public class SousEchantillonnage {

public static double[] valuesDownSample(double[] tab, int factor){

    int N = tab.length;

    double[] values = new double[N];

    for (int i = 0; i < N; i++){
        values[i] = tab[i];
    }

    int lengthDownSample = N + (facteur - 1) * (N - 1);

    double[] valuesDownSample = new double[lengthDownSample];
    for (int i = 0; i < N; i++){
        valuesDownSample[i] = values[i];
    }
    for (int i = N; i < lengthDownSample; i ++){
        valuesDownSample[i] = 0;
    }

    return valuesDownSample;
}

public static double[] indexDownSample(double[] tab, int factor){

    int N = tab.length;

    double[] indexes = new double[N];

    for (int i = 0; i < N; i++){
        indexes[i] = i;
    }

    int lengthDownSample = N + (factor - 1) * (N - 1);

    double[] indexDownSample = new double [lengthDownSample];
    for (int i = 0; i < lengthDownSample; i++){
        indexDownSample[i] = i / factor;
    }

    return indexDownSample;
}

Този метод изглежда работи.

Ето моя метод за HPS алгоритъма досега:

public static double[] hps(double[] tab){

    int N = tab.length;

    int factor2 = 2;
    int factor3 = 3;
    int factor4 = 4;
    int lengthDownSample2 = N/2 + (factor2 - 1) * (N/2 - 1);
    int lengthDownSample3 = N/2 + (factor3 - 1) * (N/2 - 1);
    int lengthDownSample4 = N/2 + (factor4 - 1) * (N/2 - 1);

            // Gives us the spectrogram of the signal tab
    double[] spectrogram = new double[N];                       
    spectrogramme = FFT.calculFFT(tab);

            // We only need the first values of the spectrogram. The other half is the same.
    double[] spectrogramCut = new double[N/2];          
    for (int i = 0; i < N/2; i++){
        spectrogramCut[i] = spectrogram[i];
    }

            // We create the array that contains the values of spectrogramCut that we downsample by a factor 2
       double[] valuesSpect2 = new double [sizeDownSamp2];  
       valuesSpect2 = SousEchantillonnage.valuesDownSample(spectrogramCut, factor2);

          // We create an array of the indexes of spectrogramCut that we downsample by a factor 2
      double[] indexSpect2 = new double[sizeDownSamp2];
      indexSpect2 = SousEchantillonnage.indexDownSample(spectrogramCut, factor2);

         // We create the array that contains the values of spectrogramCut that we downsample by a factor 3
       double[] valuesSpect3 = new double [sizeDownSamp3];  
       valuesSpect3 = SousEchantillonnage.valuesDownSample(spectrogramCut, factor3);

         // We create an array of the indexes of spectrogramCut that we downsample by a factor 3
    double[] indexSpect3 = new double[sizeDownSamp3];
    indexSpect3 = SousEchantillonnage.indexDownSample(spectrogramCut, factor3);;

       // We create the array that contains the values of spectrogramCut that we            downsample by a factor 4
   double[] valuesSpect4 = new double [sizeDownSamp4];  
   valuesSpect4 = SousEchantillonnage.valuesDownSample(spectrogramCut, factor4);

       // We create an array of the indexes of spectrogramCut that we downsample by a factor 4
    double[] indexSpect4 = new double[sizeDownSamp4];
    indexSpect4 = SousEchantillonnage.indexDownSample(spectrogramCut, factor4);

        int sizeIndex = N/2 + 5 * (N/2 - 1); // size of the array that contains all the       indexes of the downsamples

        // We create this array
    double[] indexDowSamp = new double[sizeIndex];
    indexDowSamp[0] = 0;
    indexDowSamp[1] = 0.25;
    indexDowSamp[2] = 0.333;
    indexDowSamp[3] = 0.5;
    indexDowSamp[4] = 0.666;
    indexDowSamp[5] = 0.75;

    int q = sizeIndex / 6;      // quantity of packets of 6 we can do
    int r = sizeIndex%6;        // what we are left with.

    for (int i = 6; i < q * 6; i += 6){
        for (int j = 0; j < 6; j++){
        indexDowSamp[i + j] = indexDowSamp[i + j - 6] + 1;
        }
    }
    for (int i = 0; i < r; i++){
        indexDowSamp[q * 6 + i] = indexDowSamp[q * 6 + i - 6] + 1;
    }

Заклещен съм тук. Бих искал да направя метод, който умножава заедно два масива с различна дължина.

По принцип, както можете да видите, когато понижавам дискретизацията на спектрограма, получавам два масива:

  • такъв, който има индексите, които са били намалени
  • другият, който има стойностите след понижаването.

Това, което бих искал да направя, е да създам масив със същия размер като indexDownSample: valuesDownSample. Например имаме indexDownSample[0] = 0. Бих искал да имам за valuesDownSample[0] произведението на valuesSpectCut[0] *valuesSpect2[0]*valuesSpect3[0]*valuesSpect4[0], защото всички тези масиви имат стойност, която съответства на индекса 0 (indexSpectCut[0] = 0, indexSpect2[0] = 0 = indexSpect3[0] = indexSpect4[0]).

за indexDownSample[1]=0.25, забелязваме, че само indexSpect4[1]= indexDownSample[1] = 0.25 Тогава ще имаме по подразбиране 0 за valuesDownSample[1].

И продължаваме така, докато запълним масива.

Ако всичко върви гладко, накрая трябва да имаме:

  • valuesDownSample, който съдържа различните стойности на продуктите
  • indexDownSample, който съдържа различния индекс с намалена дискретизация.

Просто ще трябва да намеря максималния пик, за да намеря основната си честота.

Единственият ми проблем е, че нямам идея как да направя умножението.....

Ако някой има идея, ще съм много благодарна!


person fireangel3000    schedule 05.05.2012    source източник
comment
Ще помогне много, ако опишете с какво всъщност имате проблеми и покажете поне част от това, което сте направили досега. Както е, точно сега трябва да познаем какво да предложим...   -  person Donal Fellows    schedule 05.05.2012
comment
защо java?!? опитайте c++, fortran, нещо друго!   -  person L7ColWinters    schedule 05.05.2012
comment
Благодаря и на двамата за отговорите. Моят проект трябва да е в java. Не е избор... Беше наложен. Докато за кода досега ето какво направих за намаляването на дискретизацията. Съжалявам, но поясненията ми ще бъдат на френски.....   -  person fireangel3000    schedule 05.05.2012


Отговори (1)


Добре, имам действителен отговор, ето как реших проблема:

Първо, аз не го наричам намаляване на дискретизацията, а разделяне на честотите на FFT. За това използвам само 2 масива така:

float findex[1000]; //index of frequencies for harmonic product spectrum
float mindex[1000]; //index of magnitudes for harmonic product spectrum
unsigned int max_findex;    //number of elements in findex[] and mindex[]

Това е моята инициализация: max_findex = 0; за (i = 0; i ‹ 1000; i++) {mindex[i] = 0.0; }

Разработвам на Analog Devices Sharc EZ Development Board в VisualDSP++, така че кодът ми е на C.

Използвам голям размер на FFT от 131072 при ниска честота на дискретизация от 12kHz. Това ми дава сравнително висока точност от 0,09Hz на bin. Тъй като по-високите хармоници ще бъдат по-точни, започвам първо с моето най-високо деление:

//first run with max division (highest accuracy)
for (i = 0; i < new_peak; i++) {
    findex[max_findex] = f(peak[i].index) / 9;
    mindex[max_findex] = peak[i].magnitude;
    if (max_findex < 999) max_findex++;
    else xmitUARTmessage("ERROR max_findex\r\n", 100);
}

Имам всички мои пикове от FFT в структура като следната: peak[].index е номерът на контейнера в FFT, а peak[].magnitude е величината на пика. Функцията f() връща честотата на bin.

След това бих преминал към раздел 8, след това 7 и т.н. и раздел 1 (действителните честоти са последни). За всеки пик, който е разделен, преглеждам своя масив, за да видя дали вече имам честота в този момент. Използвам +/- 0,2 и вероятно бих могъл да го затегна, но трябва да го настроите спрямо точността на вашия FFT.

char found;

for (u2 = 8; u2 > 0; u2--) {
    for (i = 0; i < new_peak; i++) {
        tempf = f(peak[i].index) / u2;
        found = 0;
        for (u = 0; u < max_findex; u++) {
            //try to find existing frequency
            if (tempf < findex[u] + 0.2 && tempf > findex[u] - 0.2) {
                //found existing frequency
                mindex[u] *= peak[i].magnitude;
                found = 1;
                break;
            }
        } //for u
        if (!found) {
            //no frequency was found, add new one
            findex[max_findex] = tempf;
            mindex[max_findex] = peak[i].magnitude;
            if (max_findex < 999) max_findex++;
            else xmitUARTmessage("ERROR max_findex\r\n", 100);
        }
    } //for i
} //for u2

И това е. Сега просто отпечатвам стойностите си и ги сортирам по величина в Excel...

for (i = 0; i < max_findex; i++) {
    sprintf(tempstr, "%.2f,%.2f\r\n", findex[i], mindex[i]);
    xmitUARTmessage(tempstr, 100);
}

Ето някои резултати (показвам само първите 6): 60.53 1705693250000 60 1558419875000 20 555159950 179.99 264981525 7.5 1317353 8.57 1317353

Входният аудио сигнал, който създаде изхода, беше: 60 Hz квадратна вълна (фундаментални плюс нечетни хармоници) 181,5 Hz синусоида 302,5 Hz синусоида 423,5 Hz синусоида

Симулирах честота на биене от 60 + 60,5 Hz, като фундаментът напълно липсваше при 60,5. Това е труден случай и проработи :)

person Peter    schedule 01.11.2013