ユーザー指定の周波数で正弦波を生成し、96kHz オーディオ チャネルで再生するプログラムを作成しました。いくつかの CPU サイクルを節約するために、オーディオの短いセクションをバッファーに事前レンダリングし、ループでバッファーを再生するという古いトリックを採用しています。プログラムの期間を短縮し、代わりに単純なメモリコピーを実行します。
私の問題は、この事前レンダリングされたバッファの使用可能な最小サイズを効率的に決定することです。一部の周波数では簡単です。たとえば、8kHz の正弦波は、12 サンプルのバッファを生成してループ再生することで完全に表すことができます (8000*12 == 96000)。ただし、他の周波数では、正弦波の 1 サイクルを表すには整数ではない数のサンプルが必要になるため、1 サイクル分のサンプルをループすると、許容できないグリッチが発生します。
ただし、これらの周波数の一部については、正弦波の複数のサイクルを事前にレンダリングしてループすることで、その問題を回避することができます。バッファー内のサンプル数が整数であることを保証しながら、バッファーは整数になります。たとえば、12.8kHz の正弦波周波数は 7.5 サンプルの単一サイクル バッファ サイズに変換され、きれいにループしませんが、正弦波の 2 つの連続したサイクルを 15 サンプル バッファにレンダリングすると、結果をきれいにループできます。
この問題を解決するための私の現在のアプローチは力ずくです。考えられるすべてのサイクル カウントを試して、それらのいずれかが、サンプル数が整数のバッファ サイズになるかどうかを確認します。このアプローチは、次の理由から不十分だと思います。
1) 非常に効率が悪い。たとえば、以下に示すプログラム (0Hz から 48kHz までの 480,000 の可能な周波数値のバッファー サイズの結果を出力する) は、私の 2.7GHz マシンで完了するのに 35 分かかります。これを行うには、はるかに高速な方法が必要だと思います。
2) 浮動小数点エラーのため、結果は 100% 正確ではないと思われます。
3) 10 秒未満の許容可能なバッファ サイズが見つからない場合、アルゴリズムはあきらめます。(制限を高くすることもできますが、もちろんアルゴリズムはさらに遅くなります)。
それで、できれば O(1) 時間で、使用可能な最小バッファサイズを分析的に計算する方法はありますか? 簡単なように思えますが、どのような数学を使用すればよいかわかりません。
アドバイスをよろしくお願いします!
#include <stdio.h>
#include <math.h>
static const long long SAMPLES_PER_SECOND = 96000;
static const long long MAX_ALLOWED_BUFFER_SIZE_SAMPLES = (SAMPLES_PER_SECOND * 10);
// Returns the length of the pre-render buffer needed to properly
// loop a sine wave at the given frequence, or -1 on failure.
static int GetNumCyclesNeededForPreRenderedBuffer(float freqHz)
{
double oneCycleLengthSamples = SAMPLES_PER_SECOND/freqHz;
for (int count=1; (count*oneCycleLengthSamples) < MAX_ALLOWED_BUFFER_SIZE_SAMPLES; count++)
{
double remainder = fmod(oneCycleLengthSamples*count, 1.0);
if (remainder > 0.5) remainder = 1.0-remainder;
if (remainder <= 0.0) return count;
}
return -1;
}
int main(int, char **)
{
for (int i=0; i<48000*10; i++)
{
double freqHz = ((double)i)/10.0f;
int numCyclesNeeded = GetNumCyclesNeededForPreRenderedBuffer(freqHz);
if (numCyclesNeeded >= 0)
{
double oneCycleLengthSamples = SAMPLES_PER_SECOND/freqHz;
printf("For %.1fHz, use a pre-render-buffer size of %f samples (%i cycles, %f samples/cycle)\n", freqHz, (numCyclesNeeded*oneCycleLengthSamples), numCyclesNeeded, oneCycleLengthSamples);
}
else printf("For %.1fHz, there was no suitable pre-render-buffer size under the allowed limit!\n", freqHz);
}
return 0;
}