在SDL回调函数中以特定频率播放波形

7

我有一个64个样本的波形。如果采样率为44100赫兹,如何循环播放此波形以播放任意频率?

频率=采样率/波形持续时间(样本数)

因此,频率应该是689赫兹(44100/64)。如果我想将其设置为65.41赫兹(C-2),我需要执行以下操作:

65.41 = 44100 / x

解出x约为674.208。因此,我需要找出以哪个速度来播放波形以获得这个频率。我们可以解决以下方程:

64 * x = 674.208

得到大约10.5。因此,需要以波形原始速度的10.5%来播放波形。

以下是我的代码:

double smp_index = 0;
double freq = .105;

void callback(void *data, Uint8 *buf, int len){
    int i;
    s8 *out;
    out = (s8*) buf;
    if(smp_index < waveform_length){
        for(i = 0; i < len; i ++){
            out[i] = smpdata[(int)smp_index];
            smp_index +=freq;
            if(smp_index >= waveform_length)
                smp_index = 0;
        }
    }
}

因此,产生的音频应该是关于C-2音符的,但实际上更像是D-2。这个问题是什么原因导致的呢?

(int)smp_index

导致问题的原因是什么?我没有看到任何其他解决这个问题的方法...
2个回答

2

实际上,问题的主要原因不在于您的代码,而在于您的推理。

因此,我们可以解决这个方程:

64 * x = 674.208

得到大约10.5。

至此一切顺利。(实际上674.208应该是674.246,但这是因为您之前将65.41四舍五入到4个有效数字。)

因此,波形需要以其原始速度的10.5%播放。

不!波形必须以10.5倍减速。这意味着它必须以原始速度的1/10.5=0.095或9.5%播放。


太棒了!这正是问题所在。真是时候巧合,我几乎忘记了自己最初在做什么。 - freedrull
我又遇到了一个问题。如果波形不是周期性的呢?:{ - freedrull
你需要以循环的方式重新填充smpdata[]缓冲区,或者使用双缓冲技术,这样当一个缓冲区正在播放时,第二个缓冲区正在被填充,然后它们被交换。 - Artelius

1

(int)smp_index的强制类型转换并不是引起问题的原因。它只是拉伸了波形 - 这是质量损失(也许你应该让你的波形数据长于64个样本),但绝对不可能改变频率。最有可能的问题是:

        if(smp_index > realLength)
            smp_index = 0;

应该是:

        if(smp_index >= realLength)
            smp_index -= realLength;

我还有一些其他的笔记给你:

频率 = 采样率 / 波形在样本中的持续时间

如果你所说的“波形持续时间”是指一个波的周期,那么是的。也就是说,如果你的64个样本的波形是周期为64的正弦波,那么是的。如果它是32或16,那么情况会有所不同。如果它是不能被64整除的数(如48或30),那么你的波形本身就不是周期性的。

现在:

u32 waveform_length;
out = (s8*) buf;
if(smp_index < waveform_length){

waveform_length的值是多少?看起来没有初始化...


我已经处理了将 > 替换为 >= 的问题。代码中还有一些其他的错别字,我已经修复了它们。waveform_length 长度在代码的其他地方被初始化,它是从文件中读取的,并且我知道它是正确的,因为我将我的 waveform_length 值与另一个读取文件的程序进行了比较。 - freedrull
这个波形不完全是正弦波,但我的意思是指它的周期。 - freedrull

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接