4

C で位相アキュムレータを使用して、フィードバック付きの FM 合成演算子を実装しようとしています。Tomisawa の元の特許では、加算器に入る位相アキュムレータは、-2^(n-1} で、負と正の両方のインデックスをカウントします。 pi の位相で -pi から 2^(n-1) までの正弦波位相 簡単にするために、符号なし 32 ビット整数の上位バイトを使用して、正の値のみをカウントする位相アキュムレータを使用したいと思いますサイン テーブル ルックアップへのインデックスとして。

私はこれを実験しましたが、残念ながら、フィードバックを使用するときにアルゴリズムが期待される結果を生成するようには見えません。正弦波出力を位相アキュムレータに追加すると、鋸歯状の波形が生成されるはずですが、出力正弦波 (16 ビットの符号付き整数) を符号なし位相アキュムレータに適切に追加してこれを生成する方法がわかりません。任意の提案をいただければ幸いです。

編集:

おそらくいくつかの説明が必要です。以下は、富沢の元の特許からのいくつかの図です。

アルゴリズム

出力

位相アキュムレータと正弦波出力の両方が署名されている場合、アルゴリズムは簡単に実装できます。位相アキュムレータは -1 で開始し、1 まで実行され、正弦波出力も -1 と 1 の間です。Python では、アルゴリズムは次のようになり、1000 個のサンプルが生成されます。

table = []
feedback = 0.25
accumulator = -1

for i in xrange(1000):
    output = math.sin(math.pi*(accumulator + feedback*output)
    table[i] = output
    accumulator += 0.005
    if accumulator > 1:
        accumulator = -1

次のような出力が生成されます。

出力

このアルゴリズムを C に適応させようとしています。C では、計算効率のために、位相アキュムレータを符号付き整数ではなく 32 ビットの符号なし整数にしたいと考えています。そうすれば、アキュムレータの上位バイトの最初の 2 ビットを象限インデックスとして使用し、2 番目の上位バイトを 256 個の 16 ビット サイン値の配列へのインデックスとして使用して、1024 値のサイン テーブルを作成できます。お気に入り:

XXXXXXQQ.IIIIIIII.XXXXXXXX.XXXXXXXX
      ^^ ^^^^^^^^
 quadrant    index

私の問題は、FM アルゴリズムを符号なし位相アキュムレータに適用するのが難しいことです。位相アキュムレータが符号なし 32 ビット整数であり、正弦波テーブル出力が (符号付きまたは符号なし) 16 ビット整数である場合、特許と上記の Python コードに示されているように、この形式で動作するようにアルゴリズムをどのように適応させることができますか?同じ出力を生成しますか?

4

1 に答える 1

2

まず最初に、C で pyton コードを作成してみます。

#include <stdio.h>
#include <math.h>

void main() {
    double table[1000];
    double feedback = 0.25;
    double accumulator = -1;

    int i;
    for (i=0;i<1000;i++) {
        double output = sin(M_PI*(accumulator + feedback*output));
        table[i]=output;
        accumulator += 0.005;
        if (accumulator > 1)
            accumulator = -1;
        printf("%f\n",output);
    }
}

次のステップ - sin の計算値を使用する

#include <stdio.h>
#include <math.h>

void main() {
    double table[1000];
    double feedback = 0.25;
    double accumulator = 1;

    int i;

    double sinvalue[1024];
    for (i=0;i<1024;i++) {
        sinvalue[i]=sin(M_PI*i/512);
    }

    for (i=0;i<1000;i++) {
        double output = sinvalue[(int)(512*(accumulator + feedback*output))%1024];
        printf("%0.6f %0.6f %0.6f\t",accumulator,feedback,output);
        table[i]=output;
        accumulator += 0.005;
        if (accumulator > 2)
            accumulator = 0;
        printf("%f\n",output);
    }
}    

次のステップ - sin と出力の 16 ビット値を使用します。このバージョンでは、XXXXXXQQ.IIIIIIIII.XXXXXXXX.XXXXXXXX のような「出力」の値 また、一部の精度が失われました。

#include <stdio.h>
#include <math.h>

#define ONE ((int)(2*256*256*256/M_PI))

void main() {
    double table[1000];
    double feedback = 0.25;
    double accumulator = 1;
    double accumulatorDelta = 0.005;

    unsigned int feedback_i = ONE*feedback/32768;
    unsigned int accumulator_i = ONE*accumulator;
    unsigned int accumulatorDelta_i = ONE*accumulatorDelta;

    int i;

    double sinvalue[1025];
    short int sinvalue_i[1025];
    for (i=0;i<1025;i++) {
        sinvalue[i]=sin(M_PI*i/512);
        sinvalue_i[i]=32786*sinvalue[i];
        if (sinvalue[i]*32768>32768) sinvalue_i[i]=32768;
        if (sinvalue[i]*32768<-32767) sinvalue_i[i]=-32767;
    }

    for (i=0;i<1000;i++) {

        double output = sin(M_PI*(accumulator + feedback*output));
        short int output_i = sinvalue_i[ ((unsigned int) ((accumulator_i + feedback_i*output_i)*M_PI)>>16)%1024 ];
        table[i]=output;

        accumulator += 0.005;
        if (accumulator > 2)
            accumulator = 0;

        accumulator_i += accumulatorDelta_i;
        if (accumulator_i > 2*ONE)
            accumulator_i = 0;

        printf("%f %f %04X\n",output,(float)output_i/32768,(unsigned short int)output_i);
    }
}

しかし、int->double->int の変換に時間がかかりました 1 つの定数を変更すると、象限をすばやく取得する機会が失われますが、変換から解放されます

#include <stdio.h>
#include <math.h>

#define ONE ((int)(2*256*256*256))

void main() {
    short int table[1000];

    unsigned int feedback_i = ONE*0.25/32768;
    unsigned int accumulator_i = ONE*1;
    unsigned int accumulatorDelta_i = ONE*0.005;

    int i;

    short int sinvalue_i[1025];
    for (i=0;i<1025;i++) {
        double sinvalue=sin(M_PI*i/512);
        sinvalue_i[i]=32786*sinvalue;
        if (sinvalue*32768>32768) sinvalue_i[i]=32768;
        if (sinvalue*32768<-32767) sinvalue_i[i]=-32767;
    }

    for (i=0;i<1000;i++) {
        short int output_i = sinvalue_i[ ( (accumulator_i + feedback_i*output_i)>>16)%1024 ];
        table[i]=output_i;

        accumulator_i += accumulatorDelta_i;
        if (accumulator_i > 2*ONE)
            accumulator_i = 0;

        printf("%f %04X\n",(float)output_i/32768,(unsigned short int)output_i);
    }
}    
于 2013-06-04T11:27:01.230 に答える