音波の合成とスペクトル表示

  1. 目的

    単純な音は一定の周波数の波で、これは振動のモデルから生成されます。しかし、楽器の弦を弾いたときの音は単純な音ではなく、高調波(倍音)混ざっています。特定の周波数だけの単純な音はきれいな音ではなく、複数の周波数の音が適当に配分された音が「良い音」になります。
     ここでは、音を混ぜること、混ぜた音の周波数を解析する方法を紹介します。

  2. 波形の合成

    1. 単純な音

      単純な音は一定の周波数の波で、これはおもりの振動のモデルからも生成されます。ここでは、数学関数で発生した一定の強さの音を原音として利用します。単純な音は時間の関数として
       f(t) = sin(ωt)
      と表現できます。ここで w=2πf で f は周波数です。

    2. 音の合成
      二つの周波数の音を混ぜ合わせて波形を合成します。わずかに異なる周波数の音を混ぜ合わせると、下のように「うなり」が発生します。これは、二つの音が強めあう場合と、弱めあう場合が交互に繰り返されるからです。余韻をひく「鐘」の音は、この「うなり」を伴っています。
       f(t) = sin(ω1t) + sin(ω2t) = cos(((ω12)/2)*t) * cos(((ω12)/2)*t)
      です。ω1とω2 がほぼ等しいとき、(ω12)/2)=ω となり、この音が、差の周波数で (ω12)/2 変化することになります。

    3. 音の周波数分解

      合成された音に含まれる周波数とその強さを計算する手法があります。この計算手法をフーリエ変換といいます(フーリエは人の名前)。先の「うなり」を生じている波形をフーリエ変換して解析すると、下の図のようになります。横軸は周波数、縦軸はその強さです。



    4. 任意波形の合成

      任意の波形を周波数解析し、その解析に従い波形を合成すると、元の波形を再現することができます。
      たとえば、下の鋸波を解析します。


      こんな風に、奇数次の高調波が一定の割合で混合されていることがわかります。逆にこの割合で、sin波を合成すると、鋸波になります。なめらかなsin波の組み合わせで直線的な鋸波ができるのはちょっと意外ですね。



  3. 高速フーリエ変換

    1. 高速フーリエ変換の制限事項

      フーリエ変換は、各周波数成分の強さを順に求めていくため、処理に時間がかかります。高速フーリエ変換は、フーリエ変換を高速にした手法ですが処理をするためには、次の条件を満たす必要があります。

       解析するデータの個数が2のベキ乗でなければいけないこと、
       解析するデータの個数をnとしたとき、n/2以上の周波数成分は含まれてはいけないこと

      2番目の条件は重要で、この条件を無視すると、存在しない周波数がゴースト(幽霊)のように現れます。

    2. データの設定

      高速フーリエ変換では、2のベキ乗数のデータを処理します。データ数を1024とした場合、基本周波数のsin()波の1周期分のデータを1024のデータに分割して記録します。この基本周波数のデータが、周波数1のデータとして解析されます。この4倍の周波数(1/4周期)のデータを記録すると、これは周波数4のデータとして解析されます。最大周波数の場合、最低でも周期の山と谷の2サンプルが必要ですから、1024のデータ数の場合検出できる最大の周波数は512になります。
       これ以上の周期のデータを記録すると、512で「折り返した」低い周波数の成分として解析されてしまいます。

    3. 周波数解析の手法

      ここでは詳細な手法は説明しませんが、まず、2のベキ乗サイズの波のデータを配列re[]に記録します。また、同じデータをim[]にも記録します。このデータを関数、fft_main()で処理します。処理結果はre[]とim[]に記録されます。従って、元の波形データは上書きされてしまいます。
       このデータをfft_bit()で 「ビット反転処理」 をすると、re[]とim[]に、解析結果が記録されます。このデータは複素数形式ですから、fft_amp() で両者の距離(あるいはパワー)をdat[]に計算します。これが、周波数成分になります。dat[0]に周波数0の成分、dat[1]に周波数1の成分、dat[n]に周波数nの成分がでます。周波数0の成分はいわゆる直流成分で、波としては伝搬しません。

    4. フィルタ処理

      ここでは、利用しませんが、一般にサンプリングした音の周波数解析をするには、解析するデータ数に対応して、解析可能な周波数以上の成分を取り除くいわゆる「フィルタ処理」をする必要があります。このフィルタ処理で、幽霊周波数成分が現れることを防ぎます。また、データ数は、サンプリングレートに等しい数が必要です。たとえば、44kbpsの速度,16ビットサンプリングの場合、データ数は44k/16になりますから、このサイズのバッファを用意します。これで、この半分の周波数まで解析できます。

    5. サンプリング定理

      ここで、サンプリング定理と呼ばれる重要な定理(性質)を紹介します。音声やビデオ信号は一定時間間隔でその値を数値化してディジタル記録します。このようにディジタル記録した場合、記録できる信号の最大周波数成分は、サンプリング周波数の半分以下です。サンプリング周波数以上の周波数成分が信号に含まれている場合、これをあらかじめ除去しておかないと、サンプリングした音を再生する場合、雑音として再生されることになります。この処理をフィルタ処理といいます。フィルタ処理はアナログ回路または、ディジタル処理で実現できます。ただし、ディジタルでフィルタ処理できるのは、周波数の低い音声信号に限定されます。
       この、サンプリング定理は先の周波数解析の限界と同じ原理です。

  4. プロジェクト

    1. レイアウト

      ここでは手動でなく、自動レイアウトを利用しています。メソッドfinit()を参照して下さい。

      レイアウト画面(実行できません)

    2. プロジェクト

      1. 変数
        double [] sin_tbl; //sin関数の値を記録します
        double [] cos_tbl; //cos関数の値を記録します
        double [] re; //音データを記録します、FFTを実行すると周波数データが記録されます。
        double [] im; //音データの虚数部を記録します。
        double [] dat; //周波数解析した結果の統合値が記録されます

      2. メソッド
        1. finit()
          自動レイアウトで部品を配置します。p,q2枚のpanelを生成し、pを下(south)、qを上(north)の配置します。
        2. sinq(double f, int p)
          p倍音、角度fの値
          sine波形の倍音の強さは、1/pとする>矩形波になる
          方形波は、2倍の奇数次高調波を加える
          private double sinq(double f, int p) {
            if (Wsw == 0) {                          // saw wave
             return(Math.sin(p*f)/(p*Math.PI));
            } else if (Wsw == 1) {    // square wave 倍音を加えて合成
             p--;
             return((1.0/Math.PI)*Math.sin((2*p+1)*f )/(2*p+1));
            } 
           }
        3. paint
          ini();//関数表、FFt配列
          sinq()を利用して各チャンネルは波形をre[i] に生成
          im[i] = re[i];
          FOに従い、FFTまたは波形を表示する

          FFTの場合
          fft_main(); //FFT変換処理
          fft_bit(); //ビット反転処理
          fft_amp(); //i番の値をdat[i]に
          目盛りなどを表示
          dat[]を表示

          波形の場合
          格子を表示
          波形の実部:re[x]を表示 
        4. fft_main()
          re[]、im[]、をから変換処理
          re[],im[]の値が変更される
        5. fft_bit()
          re[],im[]をbit反転処理、
          b_re[i]、b_im[]を作業領域として利用

        6. fft_amp()
          re[],im[]からpowerを計算し、dat[]に記録
    3. 実行

      ch1、ch2 ボタンで1または2のチャンネルの波形(または周波数解析)を表示します。ADDを押すと、1,2チャンネルを混合した波形(または周波数解析)を表示します。oscは波形表示、FFTは周波数解析を表示するラジオボタンです。中央の数字8..11はデータ数(最大周波数)を指定します。この値の2のベキ乗が、作成するデータ数になります。上段右は、波形を選択します。sawは鋸波。suareは方形波になります。 下段partialは高調波の段数を指定します。この段数が0のときは正弦波ですが、partialを大きくすると、高調波が加わり、saw波は鋸波になり、方形波は方形波に近づきます。表示をFFTとすると、合成される高調波を見ることができます。Levelは合成する信号の強さを設定します。
       下段右は各チャンネルの周波数を設定します。FFTで見ると、周波数を示す縦線が左右に移動します。


    4. 注意

      データ数を少なくして(n=8)、基本周波数を30以上にし、倍音(partial)をあげると(5以上)、倍音の周波数が、許容周波数(128)を超えるため、ゴースト周波数が現れます。数で、122あたりの少し短い線がゴーストです。倍音の150が許容値128を超えるため、128から折り返して右から22に現れます。このため「折り返しノイズ」と呼ばれます。


    5. ダウンロード

      このプロジェクトのソースはここにあります。また、このプロジェクト(Jbuilder)をダウンロードできます。次の行をクリックして、JFFT.exeファイルを適当なフォルダに保存します。
      ダウンロード開始
      このファイルは自己解凍型の圧縮ファイルです。このファイルを実行すると指定したフォルダに必要なファイルが生成されます。

    6. 参照

      このプログラムは
       http://www2.vc-net.ne.jp/~md440/fft/index.html
      が元版で、少し変更を加えました(作者承諾いただきました)。