我正在 iOS 中制作颗粒合成器,本质上是循环非常短的微样本,以创建新的原本不可能的声音。为此,我在 Core Audio 中设置了录制和播放功能,但是正在播放和循环播放的是来自颗粒缓冲区的短样本。
我的问题是,当我从录音中的任意点取样时,循环样本的起点和终点不一定是零交叉点。因此,这可能会导致每次重新开始样本时都会点击一次,这通常是在粒度合成中。我试图通过使用 hann 函数对样本进行窗口化来解决此问题,但是当我对此进行测试时,结果是一团糟。请注意,为了便于测试,我的颗粒缓冲区长度为 22050 个样本(1/2 秒)。
// Window over entirety of the grain
for (int i = 0; i < 22050; i++)
{
// Hann window function, this is used as it zeros at beggining and end of window
double window = 0.5 * (1.0 - cos((2.0 * M_PI * i) / (22050 - 1)));
// Applying window
windowedGrain[i] = grainBufferNew[i] * window;
}
有什么想法我哪里出错了吗?windowedGrain 缓冲区是否意味着包含比grain 缓冲区更多的样本?我写错了hann函数吗?还是我没有正确应用窗口?任何可以提供的建议将不胜感激。
干杯
更新:我已经进一步测试了这个问题,设置 window = 1 会产生不失真但不加窗的声音。我也使用了一个简单的三角形窗口函数,但这也被扭曲了,见下文
SInt16 sampleSizeGrainBuffer = sizeof(grainBufferNew)/sizeof(SInt16);
double window = 0.0;
double increment = 1.0/(sampleSizeGrainBuffer/2);
for (int i = 0; i < sampleSizeGrainBuffer; i++)
{
// Simple triangular window function
if (i<(sampleSizeGrainBuffer/2)) window += increment;
else window -= increment;
// Applying window
windowedGrain[i] = grainBufferNew[i] * window;
}
更新:错误似乎是由于最终的乘法,因为 grainBufferNew 是 SInt16 而 window 是双精度。两者需要在数学上相等才能使其正常工作。由于窗口函数不能是 SInt,因此必须首先将缓冲区数据制成双精度。然后在这个总和之后,它必须转换回一个 SInt 16。
更新:我尝试将 grainBuffer 转换为双精度,但结果仍然失真,尽管结果在终端中看起来不错。
// Applying window
windowedGrain[i] = (SInt16)(((double)grainBufferNew[i]) * window);
我还尝试将 grainBuffer 转换为双精度
// Applying window
windowedGrain[i] = ((double)grainBufferNew[i]) * window;
我尝试将窗口转换为 SInt16,但结果是静音,因为窗口函数始终为零。
// Applying window
windowedGrain[i] = grainBufferNew[i] * ((SInt16)window);
然后我尝试使用相等的两个整数的大小
windowedGrain[i] = grainBufferNew[i] * (window * sizeof(SInt16));
结果似乎再次在终端中窗口化,但输出失真