2

私はこの Web ページを音声のフォルマント トラッキングのガイドラインとして使用してきました...

http://iitg.vlab.co.in/?sub=59&brch=164&sim=615&cnt=1

フォルマント トラッキング用の単純なピーク ピッキングのために、ケプストラムを平滑化された表現に変換する最後のステップを除いて、すべてが順調に進んでいるように見えます。スペクトログラフは見栄えがよく、ケプストログラフ (それと言っていいですか? :P) も (私が知る限り) 見栄えはしますが、最終段階の結果 (滑らかなフォルマント表現) は期待したものではありません。

各ステージのサンプルをビジュアル画像としてアップロードしました...

http://imgur.com/a/62duS

このサンプルは、'beed' などの音 'i' のスピーチ用です。このサイトによると…

http://home.cc.umanitoba.ca/~robh/howto.html#formants

最初のフォルマントは約 500hz、2 番目と 3 番目のフォルマントはそれぞれ 2200hz と 2800hz になります。スペトログラフは非常によく似たものを示していますが、最後の段階では次のような結果が得られています...

F1 - 891 F2 - 1550 F3 - 2329

どんな洞察も大歓迎です。私はしばらくの間、これについてぐるぐる回っています。私のコードは次のようになります...

// set up fft parameters
UInt32 log2n = 9;
UInt32 n = 512;
UInt32 window = n;
UInt32 halfN = n/2;
UInt32 stride = 1;
FFTSetup setupReal = [appDelegate getFftSetup];
int stepSize = (hpBuffer.sampleCount-window) / quantizeCount;

// calculate volume from raw samples, because it seems more reliable that fft
UInt32 volumeWindow = 128;
volumeBuffer = malloc(sizeof(float)*quantizeCount);
int windowPos = 0;
for (int i=0; i < quantizeCount; i++) {
    windowPos += stepSize;
    float total = 0.0f;
    float max = 0.0f;
    for (int p=windowPos; p < windowPos+volumeWindow; p++) {
        total += sampleBuffer.buffer[p];
        if (sampleBuffer.buffer[p] > max)
            max = sampleBuffer.buffer[p];
    }
    volumeBuffer[i] = max;
}

// normalize volumebuffer
[FloatAudioBuffer normalizePositiveBuffer:volumeBuffer ofSize:quantizeCount];

// allocate memory for complex array
COMPLEX_SPLIT complexArray;
complexArray.realp = (float*)malloc(4096*sizeof(float));
complexArray.imagp = (float*)malloc(4096*sizeof(float));

// allocate some space for temporary hamming buffer
float *hamBuffer = malloc(n*sizeof(float));

// create spectrum and feature buffer
spectrumBuffer = malloc(sizeof(float)*halfN*quantizeCount);
formantBuffer = malloc(sizeof(float)*4096*quantizeCount);
cepstrumBuffer = malloc(sizeof(float)*halfN*quantizeCount);
lowCepstrumBuffer = malloc(sizeof(float)*featureCount*quantizeCount);
featureBuffer = malloc(sizeof(float)*featureCount*quantizeCount);

// create data point for each quantize segment
float TWOPI = 2.0f * M_PI;
for (int s=0; s < quantizeCount; s++) {
    // copy buffer data into a seperate array and apply hamming window
    int offset = (int)(s * stepSize);
    for (int i=0; i < n; i++)
        hamBuffer[i] = hpBuffer.buffer[offset+i] * ((1.0f-0.46f) - 0.46f*cos(TWOPI*i/((float)n-1.0f)));

    // configure float array into acceptable input array format (interleaved)
    vDSP_ctoz((COMPLEX*)hamBuffer, 2, &complexArray, 1, halfN);

    // run FFT
    vDSP_fft_zrip(setupReal, &complexArray, stride, log2n, FFT_FORWARD);

    // Absolute square (equivalent to mag^2)
    complexArray.imagp[0] = 0.0f;
    vDSP_zvmags(&complexArray, 1, complexArray.realp, 1, halfN);
    bzero(complexArray.imagp, (halfN) * sizeof(float));

    // scale
    float scale = 1.0f / (2.0f*(float)n);
    vDSP_vsmul(complexArray.realp, 1, &scale, complexArray.realp, 1, halfN);

    // get log of absolute values for passing to inverse FFT for cepstrum
    for (int i=0; i < halfN; i++)
        complexArray.realp[i] = logf(sqrtf(complexArray.realp[i]));

    // save this into spectrum buffer
    memcpy(&spectrumBuffer[s*halfN], complexArray.realp, halfN*sizeof(float));


    // convert spectrum to interleaved ready for inverse fft
    vDSP_ctoz((COMPLEX*)&spectrumBuffer[s*halfN], 2, &complexArray, 1, halfN/2);

    // create cepstrum
    vDSP_fft_zrip(setupReal, &complexArray, stride, log2n-1, FFT_INVERSE);

    //convert interleaved to real and straight into cepstrum buffer
    vDSP_ztoc(&complexArray, 1, (COMPLEX*)&cepstrumBuffer[s*halfN], 2, halfN/2);

    // copy first part of cepstrum into low cepstrum buffer
    memcpy(&lowCepstrumBuffer[s*featureCount], &cepstrumBuffer[s*halfN], featureCount*sizeof(float));

    // make 8000 point array based on the first 15 values
    float *tempArray = malloc(8192*sizeof(float));
    for (int i=0; i < 8192; i++) {
        if (i < 15)
            tempArray[i] = cepstrumBuffer[s*halfN+i];
        else
            tempArray[i] = 0.0f;
    }
    vDSP_ctoz((COMPLEX*)tempArray, 2, &complexArray, 1, 4096);
    float newLog2n = log2f(8192.0f);
    complexArray.imagp[0] = 0.0f;
    vDSP_fft_zrip(setupReal, &complexArray, stride, newLog2n, FFT_FORWARD);
    vDSP_zvmags(&complexArray, 1, complexArray.realp, 1, 4096);
    bzero(complexArray.imagp, (4096) * sizeof(float));

    // scale
    scale = 1.0f / (2.0f*(float)8192);
    vDSP_vsmul(complexArray.realp, 1, &scale, complexArray.realp, 1, 4096);

    // get magnitude
    for (int i=0; i < 4096; i++)
        complexArray.realp[i] = sqrtf(complexArray.realp[i]);

    // write to formant buffer
    memcpy(&formantBuffer[s*4096], complexArray.realp, 4096*sizeof(float));

    // complex array now contains formant spectrum
    // it's large, so get features here!
    // try simple peak picking algorithm for first 3 formants
    int formantIndex = 0;
    float *peaks = malloc(6*sizeof(float));
    for (int i=0; i < 6; i++)
        peaks[i] = 0.0f;
    for (int i=1; i < 4096-1 && formantIndex < 6; i++) {
        if (complexArray.realp[i-1] < complexArray.realp[i] &&
            complexArray.realp[i+1] < complexArray.realp[i])
            peaks[formantIndex++] = i;
    }
4

0 に答える 0