FFT

FFTとは何か

これまで見たように、音声はコンピュータ内部で音声信号として扱われ、音声信号とは振幅波形のデータであった。振幅波形とは言わば音響エネルギーの大きさを時系列で並べたものであり、信号の時間領域表現とも呼ばれる。

次に、周波数ごとの音響エネルギーの大きさを、周波数の順で並べたものがあり、これは周波数領域表現である。本文中でも既に何度か見ているが、これは一般にはスペクトルアナライザ(スペクトル分析器)として、音響機器や音響ソフトの表示の一部などとして知られているものである。

周波数領域表現は、聴覚の現実に即している。というのも、例えばn個の異なる周波数が同時に鳴っている場合、周波数領域表現ではn個のピークを見ることができ、また脳もn個の音として(正確な数はわからないにせよ少なくとも音色の変化として)聴覚するが、振幅波形を見てそこに何個の周波数が含まれるかは恐らくわからない。実は、内耳には特定周波数ごとに共鳴する有毛細胞が多数存在し、これにより聴覚は周波数を聴き分けているという。天然のスペクトルアナライザと言うべきである。

ところでスペクトルとは元来、光をプリズムなどの分光器で波長(周波数)順に分解したものを意味する。プリズムによって、例えば視覚にはほぼ同一である、自然光の白色(波長の全体に一様に分布し「虹色」となる)と、モニタ上の白色(赤、緑、青の三箇所にのみ細い帯状に分布する)を比較することができるが、注意すべきは、音のスペクトルの場合はプリズムのように容易ではないということである。

アナログ機器などの、簡単な周波数領域表現は多数のフィルタを並べ、各フィルタの音量の表示で実現する。限られたリソースしか持たないPCにとっては、これは力技であり、高速フーリエ変換(Fast Fourier Transform, FFT)とは、多数のフィルタを並べるよりももっとましな方法として考案された、数学的トリックである。

FFTで何ができるかと言えば、振幅波形の操作では不可能なスペクトル操作、クロス合成(ボコーダ)や自由なフィルタリングといったものだが、FFTを一から理解しようとすると、音出しに至るまでに難渋な数学を通過しなければならない。そのため本文では、音出しにとってトリヴィアルな(と思われる)数学的技術的話題を極力排することにする。

尚、pdにおけるFFTの詳細は
http://www.parasitaere-kapazitaeten.net/Pd/fft_und_pd_en.htm、FFTの数学的技術的詳細はweb上で多く見ることが出来る。またpdより自由度の高いスペクトル操作はBitmap Player(http://www.webcentre.ru/~vsoft/BitmapPlayer.htm)などで可能である。

座標への変換

#N canvas 0 0 548 329 12; #X obj 49 35 osc~; #X floatatom 49 3 0 0 0 0 Hz - -; #N canvas 560 0 426 466 fft 1; #X obj 173 68 inlet~; #X obj 23 18 block~ 256 1 1; #X obj 172 328 framp~; #X obj 173 110 rfft~; #X obj 216 418 tabsend~ amp; #X obj 40 295 tabsend~ real; #X obj 271 295 tabsend~ imag; #X obj 335 18 loadbang; #X msg 335 50 \; pd dsp 1; #X obj 105 386 /~ 128; #X obj 105 418 tabsend~ bin; #X obj 40 213 print~ real; #X obj 40 175 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X obj 269 175 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 -1; #X obj 269 213 print~ imag; #X obj 40 263 /~ 256; #X obj 271 263 /~ 256; #X connect 0 0 3 0; #X connect 2 0 9 0; #X connect 2 1 4 0; #X connect 3 0 2 0; #X connect 3 0 11 0; #X connect 3 0 15 0; #X connect 3 1 2 1; #X connect 3 1 14 0; #X connect 3 1 16 0; #X connect 7 0 8 0; #X connect 9 0 10 0; #X connect 12 0 11 0; #X connect 13 0 14 0; #X connect 15 0 5 0; #X connect 16 0 6 0; #X restore 49 186 pd fft; #N canvas 0 0 450 300 graph3 0; #X array bin 256 float 0; #X coords 0 1 255 -1 100 100 1; #X restore 241 102 graph; #N canvas 0 0 450 300 graph3 0; #X array amp 256 float 0; #X coords 0 1 255 -1 100 100 1; #X restore 403 102 graph; #N canvas 0 0 450 300 graph3 0; #X array real 256 float 0; #X coords 0 0.5 255 -0.5 100 100 1; #X restore 241 -16 graph; #N canvas 0 0 450 300 graph3 0; #X array imag 256 float 0; #X coords 0 0.5 255 -0.5 100 100 1; #X restore 403 -16 graph; #X obj 118 36 phasor~; #X obj 52 -24 hsl 128 15 0 22050 0 0 empty empty empty -2 -6 0 8 -262144 -1 -1 1700 1; #X obj 50 111 *~; #X obj 69 85 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 117 111 *~; #X obj 136 85 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1; #X connect 0 0 9 0; #X connect 1 0 0 0; #X connect 1 0 7 0; #X connect 7 0 11 0; #X connect 8 0 1 0; #X connect 9 0 2 0; #X connect 10 0 9 1; #X connect 11 0 2 0; #X connect 12 0 11 1;

まず上の例では音声信号をFFTデータに変換する「rfft~」の動作を見る。左側のメインパッチで、「osc~」の下のトグルを入れ、「osc~」の周波数を適当に指定する。四個のグラフに表示が為されるが、上の二個が「rfft~」の出力である。「rfft~」は入力された音声を、どういうわけか二つの数値リストに変換するが、一方はFFTデータの実数部(グラフ「real」)、もう一方は虚数部(グラフ「imag」)である。このように二つに分かれるのは、音声信号を平面上の点として、二次元座標に変換したことを意味する。どういう意味か。

FFTの考え方は、どのような複雑な信号でも、周波数、振幅、位相の異なるサイン波を無数に加算すれば表現可能というものであり(当然、無限の加算は技術的に不可能である)、従って音声信号を複数のサイン波に分解する。この時、分解された各サイン波の波形は、FFTでは円運動として扱われる(
fig.3)。円運動する点の平面上の位置を、縦横の座標で示したものが、FFTデータである。因みにこの平面が、実数軸と虚数軸が直交する複素数平面であるため、FFTデータの表現は「実数+虚数」という複素数になる。複素数を出力するために「rfft~」の出力は二個に分かれる。

分解する周波数の数は「block~」により指定される(2の累乗でなければならない)。この場合は信号処理の全域、0~44100Hzを256分割している。従って約172Hzごとの各ポイントで、音声信号を分解している。そして256個の数値(256個のサイン波の信号)を一挙に出力するため、そのままでは出力が膨大となり、「/~」などで出力信号を正規化(ノーマライズ)する必要がある。また、サンプリングレートが44100Hzの場合、まともに扱える波形はその半分の周波数、22050Hzまでであることに応じて、256個の数値の後半128個は常に0である。FFTデータの詳細は「print~」により、DOS窓で見られる。

因みに「framp~」はFFTデータを手っ取り早く解釈する。出力の一方はFFTポイントごとのデータの有無を、ポイントの順位で示している(グラフ「bin」)。これは音源を「phasor~」に変えることで確認できる。もう一方はポイントごとの振幅を示す(グラフ「amp」)。これは簡単な周波数領域表現である。

スペクトルへの変換

#N canvas 0 0 879 475 10; #N canvas 316 73 612 595 fft 0; #X obj 329 130 rfft~; #X obj 222 390 rifft~; #X obj 329 44 inlet~; #X obj 222 511 outlet~; #X obj 369 191 expr~ atan2($v2 \, $v1); #X obj 168 322 expr~ $v1*cos($v2); #X obj 319 322 expr~ $v1*sin($v2); #X text 410 209 real/imag -> phase; #X obj 133 511 tabsend~ out; #X obj 441 274 tabsend~ phas; #X obj 221 478 /~ 256; #X obj 169 191 expr~ sqrt($v1*$v1+$v2*$v2); #X text 225 209 real/imag -> amp; #X obj 57 281 tabsend~ amp; #X obj 32 31 block~ 256 1 1; #X obj 57 251 /~ 64; #N canvas 0 0 690 498 formulae 0; #X obj 60 329 expr~ $v1*cos($v2); #X obj 526 329 expr~ $v1*sin($v2); #X obj 406 158 expr~ atan2($v2 \, $v1); #X text 431 176 real/imag -> phase; #X obj 21 131 expr~ sqrt($v1*$v1+$v2*$v2); #X text 21 112 real/imag -> amp; #X obj 252 103 *~; #X obj 251 68 rfft~; #X obj 276 103 *~; #X obj 252 131 +~; #X obj 253 158 sqrt~; #X obj 252 338 *~; #X obj 265 311 cos~; #X obj 393 338 *~; #X obj 407 309 cos~; #X obj 407 288 +~ 0.75; #X obj 314 415 rifft~; #X text 203 328 =>; #X text 482 328 <=; #X text 207 130 =>; #X obj 407 68 rfft~; #X text 526 310 amp/phase -> imag; #X text 60 310 amp/phase -> real; #X connect 2 0 12 0; #X connect 2 0 15 0; #X connect 6 0 9 0; #X connect 7 0 6 0; #X connect 7 0 6 1; #X connect 7 1 8 0; #X connect 7 1 8 1; #X connect 8 0 9 1; #X connect 9 0 10 0; #X connect 10 0 11 0; #X connect 10 0 13 0; #X connect 11 0 16 0; #X connect 12 0 11 1; #X connect 13 0 16 1; #X connect 14 0 13 1; #X connect 15 0 14 0; #X connect 20 0 2 0; #X connect 20 1 2 1; #X restore 443 479 pd formulae; #X text 347 339 amp/phase -> imag; #X text 191 339 amp/phase -> real; #X connect 0 0 4 0; #X connect 0 0 11 0; #X connect 0 1 4 1; #X connect 0 1 11 1; #X connect 1 0 10 0; #X connect 2 0 0 0; #X connect 4 0 5 1; #X connect 4 0 6 1; #X connect 4 0 9 0; #X connect 5 0 1 0; #X connect 6 0 1 1; #X connect 10 0 3 0; #X connect 10 0 8 0; #X connect 11 0 5 0; #X connect 11 0 6 0; #X connect 11 0 15 0; #X connect 15 0 13 0; #X restore 224 55 pd fft; #X obj 634 21 loadbang; #N canvas 0 0 450 300 graph9 0; #X array amp 128 float 0; #X coords 0 1 127 -1 150 150 1; #X restore 162 -317 graph; #N canvas 0 0 450 300 graph9 0; #X array phas 128 float 0; #X coords 0 3.14 127 -3.14 150 150 1; #X restore 385 -317 graph; #N canvas 0 0 450 300 graph8 0; #X array out 256 float 0; #X coords 0 1 255 -1 150 150 1; #X restore 620 -316 graph; #X floatatom 163 -102 0 0 0 0 Hz - -; #X obj 166 -123 hsl 128 15 0 22050 0 0 empty empty empty -2 -6 0 8 -262144 -1 -1 0 1; #X obj 223 -60 phasor~; #X obj 163 -60 osc~; #X obj 286 -60 noise~; #N canvas 0 0 462 312 DAC 0; #X obj 175 248 dac~; #X obj 175 205 *~; #X obj 194 172 inlet~; #X obj 234 205 *~; #X obj 253 172 inlet~; #X obj 175 61 inlet; #X obj 175 108 lop~ 5; #X obj 69 124 > 0; #X msg 69 162 \; pd dsp \$1; #X connect 1 0 0 0; #X connect 2 0 1 1; #X connect 3 0 0 1; #X connect 4 0 3 1; #X connect 5 0 7 0; #X connect 5 0 6 0; #X connect 6 0 1 0; #X connect 6 0 3 0; #X connect 7 0 8 0; #X restore 63 100 pd DAC; #X obj 63 -46 vsl 15 128 0 1 0 0 empty empty vol 0 -8 0 8 -262144 -1 -1 0 1; #X obj 163 2 *~; #X obj 176 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 224 2 *~; #X obj 237 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 285 2 *~; #X obj 298 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X msg 634 44 \; phas ylabel 140 -3.14 3.14; #X connect 0 0 10 2; #X connect 0 0 10 1; #X connect 1 0 18 0; #X connect 5 0 7 0; #X connect 5 0 8 0; #X connect 6 0 5 0; #X connect 7 0 14 0; #X connect 8 0 12 0; #X connect 9 0 16 0; #X connect 11 0 10 0; #X connect 12 0 0 0; #X connect 13 0 12 1; #X connect 14 0 0 0; #X connect 15 0 14 1; #X connect 16 0 0 0; #X connect 17 0 16 1;

FFTデータすなわち座標に変換された音声信号は、スペクトルのデータとなるためにさらに数式による変換を要する。「pd fft」内にある「expr~」がその数式である(尚「pd formulae」に示した通り「expr~ atan2($v2, $v1)」以外は標準オブジェクトでも書ける。pd\doc\4.fft.examplesの用例は全て標準オブジェクトで書かれている)。「rfft~」の左下の式は、座標を振幅スペクトルに変換する。振幅スペクトルとは、つまりFFTにより分解された各サイン波の音量である。「rfft~」の右下の式は、座標を位相スペクトルに変換する。位相スペクトルとは、つまり各サイン波の位相角である。

言い換えれば、これらの式は直交座標として示されたFFTデータを極座標に変換する(
fig.4)。極座標とは、中心点からの距離と角度で、平面上の位置を表現するものである。この時、距離は分解されたサイン波の振幅であり、角度は分解されたサイン波の位相角である。

次に、下にある二個の「expr~」は振幅・位相スペクトルから、それぞれ直交座標の実数部と虚数部に変換する。これによってデータは再度FFTで扱える形式となり、FFTデータを音声信号に変換する「rifft~」への入力により逆FFT、つまり復元される。この例の場合は、直交座標を極座標に、極座標を直交座標に変換しただけなので、復元される音声は元の音声と変わりがないはずである。

ここで、メインパッチで適宜音出しをしてみる。グラフは左から、振幅スペクトル、位相スペクトル、出力音声である。この場合、聴こえる音は元の音源と何ら変わりがないようだが、例えば「osc~」を試した場合、振幅スペクトルのピークは大きく裾を広げている。実はこれは、FFTがブロックサイズごとに音声信号を区切って計算する時に生じる、波形の断裂から生じたノイズ成分の現れである。

窓かけ

#N canvas 0 0 879 475 10; #N canvas 316 73 600 583 fft 0; #X obj 329 130 rfft~; #X obj 222 390 rifft~; #X obj 329 44 inlet~; #X obj 222 511 outlet~; #X obj 342 72 tabreceive~ win; #X obj 369 191 expr~ atan2($v2 \, $v1); #X obj 168 322 expr~ $v1*cos($v2); #X obj 319 322 expr~ $v1*sin($v2); #X text 191 339 amp/phase -> real; #X text 410 209 real/imag -> phase; #X text 347 339 amp/phase -> imag; #X obj 133 511 tabsend~ out; #X obj 441 274 tabsend~ phas; #X obj 221 478 /~ 256; #X obj 169 191 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 329 100 *~; #X text 225 209 real/imag -> amp; #X obj 57 281 tabsend~ amp; #X obj 57 251 /~ 64; #X obj 32 31 block~ 256 1 1; #X connect 0 0 5 0; #X connect 0 0 14 0; #X connect 0 1 5 1; #X connect 0 1 14 1; #X connect 1 0 13 0; #X connect 2 0 15 0; #X connect 4 0 15 1; #X connect 5 0 6 1; #X connect 5 0 7 1; #X connect 5 0 12 0; #X connect 6 0 1 0; #X connect 7 0 1 1; #X connect 13 0 3 0; #X connect 13 0 11 0; #X connect 14 0 6 0; #X connect 14 0 7 0; #X connect 14 0 18 0; #X connect 15 0 0 0; #X connect 18 0 17 0; #X restore 224 55 pd fft; #X obj 634 21 loadbang; #N canvas 0 0 450 300 graph9 0; #X array amp 128 float 0; #X coords 0 1 127 -1 150 150 1; #X restore 162 -317 graph; #N canvas 0 0 450 300 graph9 0; #X array phas 128 float 0; #X coords 0 3.14 127 -3.14 150 150 1; #X restore 385 -317 graph; #N canvas 0 0 450 300 graph8 0; #X array out 256 float 0; #X coords 0 1 255 -1 150 150 1; #X restore 620 -316 graph; #X floatatom 163 -102 0 0 0 0 Hz - -; #X obj 166 -123 hsl 128 15 0 22050 0 0 empty empty empty -2 -6 0 8 -262144 -1 -1 1491 1; #X obj 223 -60 phasor~; #X obj 163 -60 osc~; #X obj 286 -60 noise~; #N canvas 0 0 462 312 DAC 0; #X obj 175 248 dac~; #X obj 175 205 *~; #X obj 194 172 inlet~; #X obj 234 205 *~; #X obj 253 172 inlet~; #X obj 175 61 inlet; #X obj 175 108 lop~ 5; #X obj 69 124 > 0; #X msg 69 162 \; pd dsp \$1; #X connect 1 0 0 0; #X connect 2 0 1 1; #X connect 3 0 0 1; #X connect 4 0 3 1; #X connect 5 0 7 0; #X connect 5 0 6 0; #X connect 6 0 1 0; #X connect 6 0 3 0; #X connect 7 0 8 0; #X restore 63 100 pd DAC; #X obj 63 -46 vsl 15 128 0 1 0 0 empty empty vol 0 -8 0 8 -262144 -1 -1 4200 1; #X obj 163 2 *~; #X obj 176 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 224 2 *~; #X obj 237 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 285 2 *~; #X obj 298 -19 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X msg 634 44 \; phas ylabel 140 -3.14 3.14; #N canvas 0 0 332 585 window 0; #N canvas 0 0 452 302 graph2 0; #X array win 256 float 0; #X coords 0 1 255 -1 100 100 1; #X restore 68 415 graph; #X obj 95 258 osc~; #X obj 95 198 samplerate~; #X obj 95 288 *~ -0.5; #X obj 95 318 +~ 0.5; #X msg 124 228 0; #X obj 95 228 /; #X obj 70 359 tabwrite~ win; #X obj 70 107 t b b; #X msg 173 198 \; win resize \$1; #X obj 148 173 t b f; #X msg 148 143 256; #X obj 70 59 loadbang; #X text 183 142 <- blocksize; #X obj 17 17 block~ 256 1 1; #X connect 1 0 3 0; #X connect 2 0 6 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 1 1; #X connect 6 0 1 0; #X connect 8 0 5 0; #X connect 8 0 7 0; #X connect 8 1 2 0; #X connect 8 1 11 0; #X connect 10 0 6 0; #X connect 10 1 6 1; #X connect 10 1 9 0; #X connect 11 0 10 0; #X connect 12 0 8 0; #X restore 285 55 pd window; #X connect 0 0 10 2; #X connect 0 0 10 1; #X connect 1 0 18 0; #X connect 5 0 7 0; #X connect 5 0 8 0; #X connect 6 0 5 0; #X connect 7 0 14 0; #X connect 8 0 12 0; #X connect 9 0 16 0; #X connect 11 0 10 0; #X connect 12 0 0 0; #X connect 13 0 12 1; #X connect 14 0 0 0; #X connect 15 0 14 1; #X connect 16 0 0 0; #X connect 17 0 16 1;

ブロックサイズと同期させて、入力音声にエンベロープを適用することで、波形の断裂の問題は解消される。「pd window」内のパッチが、グラフ「win」にエンベロープを書いている。そして「pd fft」内、「tabreceive~ win」によってFFTの前にエンベロープが適用されている。メインパッチのグラフ「out」でその効果が見られる。

因みにFFTにおいて、このエンベロープ適用は窓かけ、適用されるエンベロープは窓関数と呼ばれる。窓関数にはこの他にも性質の異なるいくつかの種類がある。

「osc~」の振幅スペクトルで、窓かけによりノイズ成分が取り除かれた事を確認する。副作用として、聴こえる音にはエンベロープ自体による振幅変調がかかる。この時、「pd fft」内の「block~」の第二引数を2または4などとすることで(2の累乗でなければならない)、FFTブロックの重ね合わせ(オーバーラップ)が指定され、変調音は軽減される。この窓関数の場合、オーバーラップが2より大きいと音量が増加するため、その時はさらに正規化を調整する必要がある。

スペクトルフィルタ

#N canvas 0 0 738 451 10; #X obj 111 134 bng 15 250 50 0 empty empty load 0 -6 0 8 -262144 -1 -1; #N canvas 0 0 450 300 graph4 0; #X array voice 62079 float 0; #X coords 0 1 62078 -1 200 140 1; #X restore 329 51 graph; #N canvas 0 0 466 316 DAC 0; #X obj 175 248 dac~; #X obj 175 205 *~; #X obj 194 172 inlet~; #X obj 234 205 *~; #X obj 253 172 inlet~; #X obj 175 61 inlet; #X obj 175 108 lop~ 5; #X obj 69 124 > 0; #X msg 69 162 \; pd dsp \$1; #X connect 1 0 0 0; #X connect 2 0 1 1; #X connect 3 0 0 1; #X connect 4 0 3 1; #X connect 5 0 7 0; #X connect 5 0 6 0; #X connect 6 0 1 0; #X connect 6 0 3 0; #X connect 7 0 8 0; #X restore 34 382 pd DAC; #X obj 34 236 vsl 15 128 0 1 0 0 empty empty vol 0 -8 0 8 -262144 -1 -1 12700 1; #N canvas 0 0 378 629 window 0; #N canvas 0 0 452 302 graph2 0; #X array win 512 float 0; #X coords 0 1 511 -1 100 100 1; #X restore 80 431 graph; #X obj 107 274 osc~; #X obj 107 214 samplerate~; #X obj 107 304 *~ -0.5; #X obj 107 334 +~ 0.5; #X msg 136 244 0; #X obj 107 244 /; #X obj 82 375 tabwrite~ win; #X obj 82 123 t b b; #X msg 185 214 \; win resize \$1; #X obj 160 189 t b f; #X obj 82 75 loadbang; #X text 195 158 <- blocksize; #X obj 31 16 block~ 512 1 1; #X msg 160 159 512; #X connect 1 0 3 0; #X connect 2 0 6 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 1 1; #X connect 6 0 1 0; #X connect 8 0 5 0; #X connect 8 0 7 0; #X connect 8 1 2 0; #X connect 8 1 14 0; #X connect 10 0 6 0; #X connect 10 1 6 1; #X connect 10 1 9 0; #X connect 11 0 8 0; #X connect 14 0 10 0; #X restore 173 252 pd window; #N canvas 0 0 530 654 fft 0; #X obj 325 465 rifft~; #X obj 326 589 outlet~; #X obj 340 96 tabreceive~ win; #X obj 326 52 inlet~; #X obj 326 126 *~; #X obj 326 181 rfft~; #N canvas 0 0 466 316 rec2pol 0; #X obj 270 119 expr~ atan2($v2 \, $v1); #X obj 70 119 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 71 63 inlet~; #X obj 227 63 inlet~; #X obj 71 172 outlet~; #X obj 270 172 outlet~; #X connect 0 0 5 0; #X connect 1 0 4 0; #X connect 2 0 1 0; #X connect 2 0 0 0; #X connect 3 0 1 1; #X connect 3 0 0 1; #X restore 326 215 pd rec2pol; #N canvas 0 0 470 320 pol2rec 0; #X obj 68 63 inlet~; #X obj 172 61 inlet~; #X obj 69 113 expr~ $v1*cos($v2); #X obj 220 113 expr~ $v1*sin($v2); #X obj 68 156 outlet~; #X obj 220 154 outlet~; #X connect 0 0 2 0; #X connect 0 0 3 0; #X connect 1 0 2 1; #X connect 1 0 3 1; #X connect 2 0 4 0; #X connect 3 0 5 0; #X restore 326 433 pd pol2rec; #X obj 209 181 max~ 0; #X obj 209 155 tabreceive~ filter; #X obj 325 491 /~ 512; #X obj 28 18 block~ 512 2 1; #X obj 312 274 *~; #X obj 340 533 tabreceive~ win; #X obj 326 563 *~; #X obj 313 330 *~ 1; #X obj 332 306 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 209 342 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1; #X obj 196 366 *~; #X obj 197 286 lrshift~ 10; #X obj 118 342 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1; #X obj 105 366 *~; #X obj 105 312 lrshift~ -32; #X obj 105 286 lrshift~ 32; #X connect 0 0 10 0; #X connect 2 0 4 1; #X connect 3 0 4 0; #X connect 4 0 5 0; #X connect 5 0 6 0; #X connect 5 1 6 1; #X connect 6 0 12 1; #X connect 6 0 23 0; #X connect 6 0 19 0; #X connect 6 1 7 1; #X connect 7 0 0 0; #X connect 7 1 0 1; #X connect 8 0 12 0; #X connect 9 0 8 0; #X connect 10 0 14 0; #X connect 12 0 15 0; #X connect 13 0 14 1; #X connect 14 0 1 0; #X connect 15 0 7 0; #X connect 16 0 15 1; #X connect 17 0 18 1; #X connect 18 0 7 0; #X connect 19 0 18 0; #X connect 20 0 21 1; #X connect 21 0 7 0; #X connect 22 0 21 0; #X connect 23 0 22 0; #X restore 111 252 pd fft; #N canvas 0 0 454 304 graph1 0; #X array filter 256 float 0; #X coords 0 1 255 0 512 100 1; #X restore 170 301 graph; #N canvas 521 243 368 355 loop 0; #X obj 175 138 soundfiler; #X msg 175 111 read -resize \$1 voice; #X obj 175 84 openpanel; #X obj 175 39 inlet; #X obj 175 230 *~; #X obj 214 171 expr 44100/$f1; #X obj 214 198 phasor~; #X obj 174 262 tabread4~ voice; #X obj 174 290 outlet~; #X msg 60 217 \; voice normalize; #X obj 60 192 b; #X connect 0 0 4 0; #X connect 0 0 5 0; #X connect 0 0 10 0; #X connect 1 0 0 0; #X connect 2 0 1 0; #X connect 3 0 2 0; #X connect 4 0 7 0; #X connect 5 0 6 0; #X connect 6 0 4 1; #X connect 7 0 8 0; #X connect 10 0 9 0; #X restore 111 225 pd loop; #X obj 580 228 loadbang; #X msg 580 256 \; filter const 0.1; #X connect 0 0 7 0; #X connect 3 0 2 0; #X connect 5 0 2 1; #X connect 5 0 2 2; #X connect 7 0 5 0; #X connect 8 0 9 0;

第一の用例として、振幅スペクトルのフィルタリングを挙げる。このパッチでは、loadとしたbangによって任意の音声ファイルを読み込み、ループ再生を開始する(例えばpd\doc\sound\voice.wav)。グラフ「filter」を書き変えることで、音色を変えることが出来る。

ここで、ブロックサイズは512、「filter」の配列長は256なので、約86Hz間隔、256バンドのグラフィックイコライザと言える。256個のバンドパスフィルタを並べることが果たして可能かはわからない。だが約86Hz間隔ということで、低音域の操作はさほどでもない。またグラフ幅は512ピクセルであり、1バンドが2ピクセルしかない。

「pd fft」でのFFT処理は、まずオーバーラップを2としている。次にFFTの入力と出力の双方に窓かけを行なっている。出力への窓かけはFFTとは関係がないが、音質を整える効用があるようである。次に振幅スペクトルへの「tabreceive~ filter」の乗算によりフィルタリングが行なわれる(FFTデータの変換式は「pd rec2pol」「pd pol2rec」にまとめられている)。

次にグラフでのフィルタリングとは関係なく、「lrshift~」の用例を示す。「lrshift~」は引数で指定したぶんFFTデータを下位にずらす(周波数を下げる)。つまりスペクトル移動だが、その効果はピッチシフトとは異なり興味深いものになる。また、一度FFTデータを下位にずらし、次に負数の引数で上位に戻すと、下位のFFTデータが切り取られてハイパスフィルタと同様になる。

ボコーダ

#N canvas 0 0 548 327 10; #N canvas 0 0 450 300 graph4 0; #X array voice 62079 float 0; #X coords 0 1 62078 -1 200 140 1; #X restore 289 79 graph; #N canvas 0 0 466 316 DAC 0; #X obj 175 248 dac~; #X obj 175 205 *~; #X obj 194 172 inlet~; #X obj 234 205 *~; #X obj 253 172 inlet~; #X obj 175 61 inlet; #X obj 175 108 lop~ 5; #X obj 69 124 > 0; #X msg 69 162 \; pd dsp \$1; #X connect 1 0 0 0; #X connect 2 0 1 1; #X connect 3 0 0 1; #X connect 4 0 3 1; #X connect 5 0 7 0; #X connect 5 0 6 0; #X connect 6 0 1 0; #X connect 6 0 3 0; #X connect 7 0 8 0; #X restore 30 244 pd DAC; #X obj 30 98 vsl 15 128 0 1 0 0 empty empty vol 0 -8 0 8 -262144 -1 -1 10200 1; #N canvas 643 0 303 562 window 0; #N canvas 0 0 452 302 graph2 0; #X array win 512 float 0; #X coords 0 1 511 -1 100 100 1; #X restore 80 431 graph; #X obj 107 274 osc~; #X obj 107 214 samplerate~; #X obj 107 304 *~ -0.5; #X obj 107 334 +~ 0.5; #X msg 136 244 0; #X obj 107 244 /; #X obj 82 375 tabwrite~ win; #X obj 82 123 t b b; #X msg 185 214 \; win resize \$1; #X obj 160 189 t b f; #X obj 82 75 loadbang; #X obj 31 16 block~ 512 1 1; #X msg 160 159 512; #X text 195 158 <- blocksize; #X connect 1 0 3 0; #X connect 2 0 6 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 1 1; #X connect 6 0 1 0; #X connect 8 0 5 0; #X connect 8 0 7 0; #X connect 8 1 2 0; #X connect 8 1 13 0; #X connect 10 0 6 0; #X connect 10 1 9 0; #X connect 10 1 6 1; #X connect 11 0 8 0; #X connect 13 0 10 0; #X restore 159 118 pd window; #N canvas 0 0 301 356 chord 0; #X obj 45 148 phasor~; #X obj 45 92 + 60; #X obj 101 148 phasor~; #X obj 101 92 + 4; #X obj 157 148 phasor~; #X obj 157 120 mtof; #X obj 213 148 phasor~; #X obj 213 120 mtof; #X obj 213 92 + 4; #X obj 157 92 + 3; #X obj 45 60 inlet; #X obj 114 302 outlet~; #X obj 114 215 /~ 4; #X obj 114 257 lop~ 3000; #X obj 45 120 mtof; #X obj 101 120 mtof; #X msg 102 59 0; #X obj 102 31 loadbang; #X connect 0 0 12 0; #X connect 1 0 3 0; #X connect 1 0 14 0; #X connect 2 0 12 0; #X connect 3 0 9 0; #X connect 3 0 15 0; #X connect 4 0 12 0; #X connect 5 0 4 0; #X connect 6 0 12 0; #X connect 7 0 6 0; #X connect 8 0 7 0; #X connect 9 0 5 0; #X connect 9 0 8 0; #X connect 10 0 1 0; #X connect 12 0 13 0; #X connect 13 0 11 0; #X connect 14 0 0 0; #X connect 15 0 2 0; #X connect 16 0 1 0; #X connect 17 0 16 0; #X restore 161 79 pd chord; #X floatatom 161 48 5 0 0 2 transpose - -; #N canvas 254 0 361 493 fft 0; #X obj 63 364 rifft~; #X obj 63 426 outlet~; #X obj 195 52 tabreceive~ win; #X obj 29 52 inlet~; #X obj 29 112 *~; #X obj 29 142 rfft~; #X obj 113 52 inlet~; #X obj 113 112 *~; #X obj 113 142 rfft~; #X obj 189 294 tabreceive~ win; #N canvas 0 0 470 320 rec2pol 0; #X obj 270 119 expr~ atan2($v2 \, $v1); #X obj 70 119 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 71 63 inlet~; #X obj 227 63 inlet~; #X obj 71 172 outlet~; #X obj 270 172 outlet~; #X connect 0 0 5 0; #X connect 1 0 4 0; #X connect 2 0 1 0; #X connect 2 0 0 0; #X connect 3 0 1 1; #X connect 3 0 0 1; #X restore 30 177 pd rec2pol; #N canvas 0 0 478 328 pol2rec 0; #X obj 68 63 inlet~; #X obj 172 61 inlet~; #X obj 69 113 expr~ $v1*cos($v2); #X obj 220 113 expr~ $v1*sin($v2); #X obj 68 156 outlet~; #X obj 220 154 outlet~; #X connect 0 0 2 0; #X connect 0 0 3 0; #X connect 1 0 2 1; #X connect 1 0 3 1; #X connect 2 0 4 0; #X connect 3 0 5 0; #X restore 63 284 pd pol2rec; #N canvas 0 0 470 320 rec2pol 0; #X obj 270 119 expr~ atan2($v2 \, $v1); #X obj 70 119 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 71 63 inlet~; #X obj 227 63 inlet~; #X obj 71 172 outlet~; #X obj 270 172 outlet~; #X connect 0 0 5 0; #X connect 1 0 4 0; #X connect 2 0 1 0; #X connect 2 0 0 0; #X connect 3 0 1 1; #X connect 3 0 0 1; #X restore 114 177 pd rec2pol; #X obj 84 229 *~; #X obj 63 334 *~; #X obj 118 334 *~; #X obj 63 393 /~ 64; #X obj 29 9 block~ 512 4 1; #X connect 0 0 16 0; #X connect 2 0 4 1; #X connect 2 0 7 1; #X connect 3 0 4 0; #X connect 4 0 5 0; #X connect 5 0 10 0; #X connect 5 1 10 1; #X connect 6 0 7 0; #X connect 7 0 8 0; #X connect 8 0 12 0; #X connect 8 1 12 1; #X connect 9 0 14 1; #X connect 9 0 15 1; #X connect 10 0 13 0; #X connect 11 0 14 0; #X connect 11 1 15 0; #X connect 12 0 13 1; #X connect 12 1 11 1; #X connect 13 0 11 0; #X connect 14 0 0 0; #X connect 15 0 0 1; #X connect 16 0 1 0; #X restore 101 118 pd fft; #X obj 102 48 bng 15 250 50 0 empty empty load 0 -6 0 8 -262144 -1 -1; #N canvas 28 329 302 312 loop 0; #X obj 135 113 soundfiler; #X msg 135 86 read -resize \$1 voice; #X obj 135 59 openpanel; #X obj 135 14 inlet; #X obj 135 205 *~; #X obj 174 146 expr 44100/$f1; #X obj 174 173 phasor~; #X obj 134 237 tabread4~ voice; #X obj 134 265 outlet~; #X msg 20 192 \; voice normalize; #X obj 20 167 b; #X connect 0 0 4 0; #X connect 0 0 5 0; #X connect 0 0 10 0; #X connect 1 0 0 0; #X connect 2 0 1 0; #X connect 3 0 2 0; #X connect 4 0 7 0; #X connect 5 0 6 0; #X connect 6 0 4 1; #X connect 7 0 8 0; #X connect 10 0 9 0; #X restore 102 79 pd loop; #X connect 2 0 1 0; #X connect 4 0 6 1; #X connect 5 0 4 0; #X connect 6 0 1 1; #X connect 6 0 1 2; #X connect 7 0 8 0; #X connect 8 0 6 0;

第二の用例はいわゆるボコーダだが、「pd fft」でのFFT処理は主に振幅スペクトルの乗算であり、原理的には先程のフィルタリングと同様である。しかしここでは「rifft~」前にも窓かけがあったり、オーバーラップが4であったり、正規化の値が違ったりと、先程とは様相が異なる。恐らく「pd chord」に含まれる「phasor~」が原因と思われるが、先程までと同様の処理を行った場合、FFTの結果で非常に音質が悪化するため、試行錯誤の末このような設定に辿り着いている。

何が起こるか容易に想定できないため、FFTの使用は比較的難しいと思うが、例えば作法に反する結線などが、興味深い結果になることもある。また既存のパッチから必要な部分を引用することも奨励できる。これはFFTに限らずpd全般に言える。