有限带宽波形生成

11
我正在编写软件合成器,并需要以44.1 kHz采样率实时生成无混叠波形。目前暂时用锯齿波,因为我可以通过将两个反相且相位转换的锯齿波混合在一起来生成脉冲波。
到目前为止,我尝试了以下方法:
1.在启动时预先计算不同带宽频率下的一周期完美无混叠波形样本,然后播放混合在一起的两个最接近的样本。我猜还可以,但感觉不太优雅。需要大量的样本,否则会听到"间隙"。插值和混合也相当占用CPU。
2.集成直流补偿sinc脉冲序列以获取锯齿波。听起来很好,除非你没有精确地补偿直流(我发现这非常棘手)。可以通过向积分器添加一点泄漏来减少DC问题,但这样就失去了低频。
所以,我的问题是:通常是怎么做的?任何建议的解决方案都必须在CPU效率上高效,因为它必须实时进行处理,对于多个声音同时进行处理。

我知道这个问题是一年前提出的,但对于其他任何人,我建议在KVR的友好和高素质的数字信号处理和插件开发论坛上进行搜索。 - Ernst Hot
4个回答

8

一种快速生成带限波形的方法是使用带限阶跃(BLEP)。您可以生成带限阶跃本身:

enter image description here

将其存储在波表中,然后用带限步进替换每个过渡,以创建类似于以下波形的波形:

enter image description here

请参阅带限声音合成的演示。
由于此BLEP是非因果的(意味着它延伸到未来),为了生成实时波形,最好使用最小相位带限步进,称为MinBLEP,它具有相同的频谱,但仅延伸到过去:

MinBLEPs进一步发展了这个想法, 取一个窗口化的sinc函数,进行 最小相位重建,然后 对结果进行积分并存储在 表格中。现在要制作振荡器, 您只需在波形的每个不连续处插入MinBLEP。 因此,对于正弦方波,您在 波形反转的位置插入MinBLEP, 对于锯齿波,您在值反转的位置插入MinBLEP, 但您正常生成斜坡。


5
生成带限波形有很多方法,通常你会在计算成本和质量之间进行权衡。建议查看此网站:http://www.musicdsp.org/,里面有很多好的资料。只需搜索关键词“bandlimited”,就可以找到许多相关资料,让你忙上至少一个星期。
顺便说一句 - 不知道这是否符合你的要求,但是我几年前做过减少别名(例如不真正的带限)波形生成。 我只是计算了最后一个和当前采样位置之间的积分。 对于传统合成器波形,如果在奇点处(例如sawtooth重置时)分割积分区间,则可以相对容易地完成。 CPU负载低,质量对我的需求来说也是可以接受的。
我遇到了同样的漂移问题,但在积分上应用非常低的截止频率的高通滤波器可以消除这种效果。真正的模拟合成器也不会降至亚赫兹区域,所以你不会错过什么。

"phase += (sampleRate/(float TableSize)/frequency);"无法编译。应该改为"(sampleRate/(float TableSize))/frequency;"吗?" - user877329
好链接!充满了许多不错的小片段。 - SvaLopLop

3
这是我灵感来源于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);
}

1
从BLIT中得到的直流偏移量可以通过简单的高通滤波器来降低!就像真正的模拟电路中使用直流阻塞电容器一样!

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