[vDSP][信号処理]オーディオ・音声分析への道10 ケプストラム分析02

#オーディオ・音声分析への道10 ケプストラム分析02

前回はCepstrumを求める所、手順5まで書きました。
今回は手順6以降をやります。

##手順のおさらい

1. オーディオデータからFFTサイズ分のデータを取得
2. 窓関数をかける
3. FFTを行う
4. 対数スペクトラルをとる
5. iFFTを行う
6. リフタリングを行い、低Quefrencyと高Quefrencyを分離する
7. 低Quefrencyを再度FFTする事でフォルマント成分を取得
8. 高Quefrencyを再度FFTする事で周波数成分を取得

##手順6 リフタリングを行う

対数スペクトラルをiFFTして得られたCepstrumに対してリフタリングを行います。ここでリフタリングとは、Cepstrumに対してフィルタリングを行う事になります。

また、QuefrencyはCepstrum領域に於けるFrequencyを意味します。

単純なスペクトラルのフィルタリングは、[前のこの記事](http://qiita.com/programanx1/items/bae730bf77775543ec7f)を参考にしてください。

名前は違えど、要領は同じです。
Cepstrumは以下の様になっています。

![cepstrum6.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/b4ef8e6c-f9bb-f2ed-1007-5028bb812d86.jpeg)

Cepstrumの横軸はQuefrency(単位はms.)です。Quefrencyは横幅が1.0と考える為、ビン番号の逆数によって周波数を得る事ができます。

音声信号のフォルマント成分は、Cepstrumの低Quefrency部分に含まれています。まずは初めの20ビンのみを抽出します。

リフタリングの際の注意事項は、FFTフルサイズのデータをリフタリングしなければならない点です。
Cepstrumは線対称になっているので、初めの20ビンのみをリフタリングする場合、最後の20ビンも同様にリフタリングします。

低Quefrency成分と高Quefrency成分を格納する領域を確保し、cepstrumの値をそれぞれコピーします。サイズはfftsize分です。

```C:main.c
float *liftering_low = malloc(sizeof(float)*fft.fftsize);
float *liftering_high = malloc(sizeof(float)*fft.fftsize);
memcpy(liftering_high,cepstrum,fft.fftsize*sizeof(float));
memcpy(liftering_low,cepstrum,fft.fftsize*sizeof(float));
```

**vDSP_vclr()** は指定データ領域に0を代入します。
第一引数にポインタ、第二引数にストライド、第三引数にデータの長さを入れます。

```C:main.c
//low
vDSP_vclr(&liftering_low[20],1,(fft.fftsize-40));
//high
vDSP_vclr(liftering_high,1,20);
vDSP_vclr(&liftering_high[2028],1,20);
```

結果は低Quefrency部分のCepstrum (FFTフルサイズ線対称)

![cepstrum8.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/5facd069-7d9f-96f1-11d2-71603458a837.jpeg)

高Quefrency部分のCepstrum (FFTフルサイズ線対称)

![cepstrum7.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/2efd7390-db3b-ad05-6056-7ee4abd58b1f.jpeg)

リフタリングは以上です。

##手順7,8 高低Quefrencyを再度FFTする事でフォルマント成分を取得

リフタリングで分離したCepstrumを、再度スペクトラルに戻します。
iFFTでCepstrumにしたのですから、元に戻す場合はFFTをします。

リフタリングをFFTする場合は、窓がけを行いません。
しかしながら、私がライブラリ化したFFT()、中で窓かけをしておりました。うーん...参った！

仕方ないので、vDSPのFFT関数を使います。とは言え、二行済みます。

```C:main.c
//low quefrency
vDSP_ctoz( ( COMPLEX * ) low_quefrency, 2, &splitComplex, 1, ffthalfsize );
vDSP_fft_zrip(kefftSetup, &splitComplex, 1, log2n,FFT_FORWARD);
```

```C:main.c
//high quefrency
vDSP_ctoz( ( COMPLEX * ) high_quefrency, 2, &splitComplex, 1, ffthalfsize );
vDSP_fft_zrip(kefftSetup, &splitComplex, 1, log2n,FFT_FORWARD);
```

リフタリング後のスペクトラルは、 **Complex型** の実数部を使用します。

実数部をプロットすると以下のようになります。

low quefrency(littering 20) : フォルマント成分
![cepstrum9.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/d1bea8f0-6648-8673-966d-e19d5d89be9d.jpeg)

high frequency : 周波数成分
![cepstrum10.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/b82de46f-f9d3-5d0e-f2a8-ae88b750a7a3.jpeg)

ここで、対数スペクタルと比べてみます。

![cepstrum2.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/e404675b-529f-7a5b-4af2-ecad174bfb67.jpeg)

![cepstrum9.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/38867db2-56e7-06f3-7e8e-823627159dd6.jpeg)

上の二つの図を見比べて分かる様に、フォルマント成分は対数スペクトラルの輪郭を表しています。最初の三つの山のピークをフォルマント成分として取得します。

それでは、リフタリングの値を変化させてみます。
現在は20ビンでリフタリングしています。
それを序々に50ビン、100ビン、200ビンと値を増やして結果をプロットしてみます。

low quefrency (littering 50bin)
![cepstrum12_50.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/dc9ccbaa-965a-0af1-c078-4a87d680faf4.jpeg)

low quefrency (littering 100bin)

![cepstrum12_100.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/bb31fcff-a7cb-f4bb-6e65-3dcdd539a8ea.jpeg)

low quefrency (littering 200bin)

![cepstrum12_200.jpg](https://qiita-image-store.s3.amazonaws.com/0/46509/d4817bd4-b15d-52bf-9b5c-d562a5a3271d.jpeg)

図を見て分かる様に、リフタリングの位置を増やすと高周波数成分が増え、結果として対数スペクトラルに輪郭が似てきます。

#まとめ
以上により、音声信号からフォルマント成分と周波数成分に分解する事ができました。フォルマントを分析する事で、母音の種類が判定できます。フォルマント成分を抽出するやり方としては他に、LPC分析法などがあります。
異なる周波数成分と、フォルマント成分を掛け合わせる事で音声合成も行う事ができます。
また、フォルマント情報をメル尺度に合わせ圧縮し、データ量を少なくする方法もあります。