0

ダブルスを使用しているときに精度が低下し、精度が低下している場所を見つけることができないようです。私はソフトウェアシンセサイザーを書いていますが、何らかの理由でオシレーター(音波発生器)の入力周波数が途中で大きくエイリアシングされます。オシレーターは三角波を生成することになっていて、正しい周波数がメソッドに渡されますが、結果がスピーカーで再生されると、音がさまざまな周波数にスナップするのがはっきりと聞こえます。

私はNAudio(http://naudio.codeplex.com/)を使用しており、このメソッドは、生成するサンプルごとに1回実行されます。

コードは次のとおりです。

double counter = 0;
int samplecounter = 0;

public double GetNextTriangle(double frequency)
{
    double samplesperwave = (double)Parent.WaveFormat.SampleRate / frequency;
    double length = (double)samplecounter / samplesperwave;
    if (length.CompareTo(0.25) == -1)
    {
        counter = length * 4.0;
    }
    else if (length.CompareTo(0.75) == -1)
    {
        counter = 1.0 - (length - 0.25) * 4.0;
    }
    else
    {
        counter = -1.0 + (length - 0.75) * 4.0;
    }
    samplecounter++;
    samplecounter = samplecounter > samplesperwave ? 0 : samplecounter;
    return counter;
}

前もって感謝します!//マット

4

4 に答える 4

2

あなたの問題は精度の問題ではありません。問題は、関数が三角波を定義していないことです。毎回samplecounter0にリセットされると、関数からの次の戻り値は0になります。次回のラウンド長は0に設定され、最初の分岐が実行され、カウンターが0に設定されます。

このような問題に直面した場合は、出力をプロットする必要があります。そうしていたら、関数が三角波を生成しないことがすぐにわかります。これを行うコードは次のとおりです。

static double GetNextTriangle(double frequency)
{
    double samplesperwave = Parent.WaveFormat.SampleRate / frequency;
    double t = samplecounter / samplesperwave;
    counter = 1.0 - 4.0 * Math.Abs(Math.Round(t - 0.25) - (t - 0.25));
    samplecounter++;
    return counter;
}

繰り返しになりますが、コードの動作を洞察するために、コードからの出力をプロットして視覚化する必要があることを強調することはできません。

于 2012-11-06T21:45:46.473 に答える
2

何が悪いのかをよりよく理解するために、データを視覚化してみましょう。これは、毎秒41サンプルの2Hz波の2サイクルです。

ここに画像の説明を入力してください

サンプル20の周りにブリップがあります。サンプルが三角波に適切に適合していないようです。

三角波の計算を見てみましょう。あなたがしているのは、離散点で連続関数をサンプリングすることです。fまず、周波数(または)の連続三角波1/Tをヘルツで定義します。

tri(t) =   {  4*f*t,            0     <= t < T/4   }
       =   { -4*f*(t - T/2),    T/4   <= t < 3*T/4 }
       =   {  4*f*(t - T),      3*T/4 <= t < T     }
       =   tri(t - T)  [it's periodic!]

ここで、この連続関数をサンプリングします。したがって、サンプリングレートs(または1/U)を1秒あたりのサンプル数で定義します。これで、nthサンプルは単純に次のようになりますtri(n*U)

tri[n] = tri(n*U)
       =   {  4*f*n*U,            0     <= n*U < T/4   }
       =   { -4*f*(n*U - T/2),    T/4   <= n*U < 3*T/4 }
       =   {  4*f*(n*U - T),      3*T/4 <= n*U < T     }

P = T/U正規化された周期、、および正規化された頻度を定義することによって、それを少しクリーンアップしましょうF = f/s = U/T

tri[n] =   {  4*F*n,            0     <= n < P/4   }
       =   { -4*F*(n - P/2),    P/4   <= n < 3*P/4 }
       =   {  4*F*(n - P),      3*P/4 <= n < P     }

今、私たちはこれを手に入れます:

ここに画像の説明を入力してください

先端の明らかな「ブリップ」について心配する必要はありません。それらは予想されるものであり、回避しようとすべきではありません。

コードは次のとおりです。

public static double GetNextTriangle(int sample, double frequency, double sampleRate)
{
    double T = 1d / frequency;
    double U = 1d / sampleRate;

    double P = T / U;
    double F = U / T;

    double n = (double)sample;
    n %= P; // restrict n to the domain [0, P)

    if ((n >= 0) && (n < (P / 4d)))
    {
        return 4d * F * n;
    }
    else if ((n >= (P / 4d)) && (n < (3d * P / 4d)))
    {
        return -4d * F * (n - (P / 2d));
    }
    else // if ((n >= (3d * P / 4d)) && (n < P))
    {
        return 4d * F * (n - P);
    }
}
于 2012-11-06T22:43:35.073 に答える
2

ここでの浮動小数点演算は適切に調整されています。ただし、1/frequencyが1/sampleRateの倍数でない場合は、サンプリングに関連する問題が発生する可能性があります。

可能であれば、このmatlabコードを試してください

sampleRate=5000;
frequency=700;
sampleperwave=sampleRate/frequency;
samplecounter=0:floor(sampleperwave);
samplecounter=repmat(samplecounter,1,5);
length=samplecounter/sampleperwave;
wave=-1+4*(length-0.75);
wave(length<0.75)=1-4*(length(length<0.75)-0.25);
wave(length<0.25)=4*length(length<0.25);
figure; stem(wave); hold on; plot(wave,'r')

不連続

samplecounterをdoubleとして宣言し、モジュロでインクリメントすることができます

samplecounter++;
samplecounter = samplecounter % samplesperwave;

またはMATLABに戻る

samplecounter=0:length(samplecounter)-1;
samplecounter=rem(samplecounter,sampleperwave);
len=samplecounter/sampleperwave;
wave=-1+4*(len-0.75);
wave(len<0.75)=1-4*(len(len<0.75)-0.25);
wave(len<0.25)=4*len(len<0.25);
figure; stem(wave); hold on; plot(wave,'r')

エイリアシング

于 2012-11-06T23:47:56.103 に答える
0

全体のアプローチは間違っているようです。私はこのようなもので行きます:

public class RectWave
{ // triangular wave in range [0,1] with given frequency
    public double GetNextSample()
    {
        if (_ptr == _wave.Length)
            _ptr = 0;
        return _wave[_ptr++];
    }

    public RectWave(int sampleRate = 441000, int period = 20)
    { 
        _sampleRate = sampleRate;
        _period = period;
        _wave = new double[(int)(_sampleRate * _period / 1000.0)];  // one cycle
        _ptr = 0;
        BuildWave();
    }

    private void BuildWave()
    {
        double dt = 1000.0 / _sampleRate;   // in msec
        double slope = 1.0 / (_period / 2.0);   // 1.0 => peek 
        for (int i=0; i <= _wave.Length/2; ++i)
        {
            _wave[i] = _wave[_wave.Length - i - 1] = i * dt * slope; 
        }
    }

    private int _sampleRate;    // in Hz
    private int _period;        // in msec
    private double[] _wave;     // the data
    private int _ptr;           // current sample
}
于 2012-11-06T22:40:45.280 に答える