这就是我在 Nils 想法的启发下想到的。将其粘贴在这里以防对其他人有用。我只是使用最后一个样本的相位变化作为内核大小(或截止值)对锯齿波进行分析框过滤。它工作得相当好,在最高音符处有一些听得见的混叠,但对于正常使用来说,它听起来很棒。
为了进一步减少混叠,可以稍微增加内核大小,例如使其 2*phaseChange 听起来也不错,尽管您会丢失一些最高频率。
此外,这是我在浏览 SP 以查找类似主题时发现的另一个很好的 DSP 资源:The Synthesis ToolKit in C++ (STK)。它是一个类库,有很多有用的 DSP 工具。它甚至可以使用带限波形发生器。他们使用的方法是像我在第一篇文章中描述的那样集成 sinc(尽管我猜他们做得比我好......)。
float getSaw(float phaseChange)
{
static float phase = 0.0f;
phase = fmod(phase + phaseChange, 1.0f);
return getBoxFilteredSaw(phase, phaseChange);
}
float getPulse(float phaseChange, float pulseWidth)
{
static float phase = 0.0f;
phase = fmod(phase + phaseChange, 1.0f);
return getBoxFilteredSaw(phase, phaseChange) - getBoxFilteredSaw(fmod(phase + pulseWidth, 1.0f), phaseChange);
}
float getBoxFilteredSaw(float phase, float kernelSize)
{
float a, b;
// Check if kernel is longer that one cycle
if (kernelSize >= 1.0f) {
return 0.0f;
}
// Remap phase and kernelSize from [0.0, 1.0] to [-1.0, 1.0]
kernelSize *= 2.0f;
phase = phase * 2.0f - 1.0f;
if (phase + kernelSize > 1.0f)
{
// Kernel wraps around edge of [-1.0, 1.0]
a = phase;
b = phase + kernelSize - 2.0f;
}
else
{
// Kernel fits nicely in [-1.0, 1.0]
a = phase;
b = phase + kernelSize;
}
// Integrate and divide with kernelSize
return (b * b - a * a) / (2.0f * kernelSize);
}