2012-03-06 6 views
3

私はこれと似たようなことが必要なので、このexample about sound generation on iOSを見ていますが、理解できない部分があり、誰かがそれを手伝ってくれることを望んでいました。コードのこの部分でiOSの音色の生成

double theta_increment = 2.0 * M_PI * viewController->frequency/viewController->sampleRate; 
    // Generate the samples 
    for (UInt32 frame = 0; frame < inNumberFrames; frame++) 
    { 
     buffer[frame] = sin(theta) * amplitude; 

     theta += theta_increment; 
     if (theta > 2.0 * M_PI) 
     { 
      theta -= 2.0 * M_PI; 
     } 
    } 

は、私は本当にtheta += theta_increment;部分が何のためにあるのかを理解していません。私にとっては、forループの内部でこれを行う方が意味があります。

buffer[frame] = sin(theta_increment * frame); 

なぜそれがうまくいかないのでしょうか?また、私はコードのこの部分が何であるか分かりません:if (theta > 2.0 * M_PI)それについての説明も非常に歓迎されるでしょう。

答えて

1

あなたのアプローチを使用して同じ結果を作成することができます。しかし、theta += theta_increment;は、あなたが提案したものよりも(計算するのに)簡単な表現になります。

フェーズのドメインは、論理パラメータドメインsin'にラップされます。このステップは、短いサンプルには本当に必要ではありません。浮動小数点ストレージの制限により、生成するサンプルの数とfloatまたはdoubleのどちらを使用するかによって、値がラップされていない場合は最終的に変化する可能性があります。これを次のように考えてみてください:の巨大な浮動小数点数(位相アキュムレータの値)があり、0.000004を追加しようとするとどうなりますか?浮動小数点誤差は、浮動小数点数または2倍数に収まるように丸めます。誤差は位相を返し、最終的にピッチの不安定性をもたらします。短いサンプル(例えば、いくつかのサイクル)では、この場合にはラップは必要ではないが、多くの多くのサイクルでは、時間の経過とともにピッチおよび位相アキュムレータを安定させる働きをする。

最後に、位相ランプの最終値を格納するためにthetaが使用され、次のレンダー呼び出しで中断した部分の生成を再開します。それがなければ、出力はレンダリング呼び出しの境界で0で再開し、非常に不快なノイズと誤った周波数を生成します。

すべてが考慮されています:シンプルなデモであり、そのコンテキストでサインを生成するための簡単な方法だった可能性があります。あなたのアプローチにはコストのかかる変換がありますが、ブランチレスです。より高い周波数の場合は元のものより高速です。

1

私はそれだけであなたの方法を細かく(ただし、振幅を乗算することを忘れないでください)働くだろうと想像します。同じ数学を表現する

buffer[frame] = sin(theta_increment * frame) * amplitude; 

わずか2種類の方法。元のコードを書いた人のように0 < = theta < 2piを維持していたようですが、sin()呼び出しがわからない限り、これはおそらく必要ありません。また、他のループで同じスニペットが他のループに現れる場合には、フレーム変数から独立した "数学"部分を保持したいかもしれませんが、それは投機的なことです。

関連する問題