首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带限波形产生

带限波形产生
EN

Stack Overflow用户
提问于 2008-10-06 17:31:43
回答 4查看 7.4K关注 0票数 11

我正在编写一个软件合成器,需要在44.1 kHz采样时实时生成带限、别名自由的波形。锯齿波现在可以用,因为我可以通过把两个锯齿混在一起产生一个脉冲波,一个倒移和相移。

到目前为止,我已经尝试了以下方法:

  1. 在启动时,预先计算一个周期的完全带限波形样本,在不同的频带限制频率下,然后回放两个最近的波形混合在一起。我想工作还不错,但感觉不太优雅。需要大量的样本,否则它们之间的“缺口”就会被听到。内插和混合也是相当密集的CPU。
  2. 将直流补偿的正弦脉冲串积分,得到锯齿波。听起来很棒,但如果你没有得到完全正确的直流补偿(我发现这是非常棘手的),那么波就会从零开始漂移。直流问题可以通过在积分器上增加一些泄漏来减少,但是这样你就失去了低频。

所以,我的问题是:通常的做法是什么?任何建议的解决方案都必须在CPU方面是有效的,因为它必须实时地对多个声音同时进行。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2008-10-06 17:49:42

带限波形的产生方法有很多种。你将结束交易的计算成本与质量与往常一样。

我建议你在这里看看这个网站:

http://www.musicdsp.org/

查查档案!里面全是好材料。我刚搜索了关键字“频限”。如果你忙碌了至少一周,弹出的材料就会弹出来。

顺便说一句--不知道这是不是你想要的,但几年前我确实减少了别名的波形生成(例如,并不是真正的波段限制)。我刚刚计算了最后一个样本位置和当前样本位置之间的积分。对于传统的合成器-波形,如果你在奇点处分裂你的积分间隔(例如锯齿得到他的重置),你可以很容易地做到这一点。CPU负载低,质量可以满足我的需要。

我也有同样的漂移问题,但是在积分上应用一个很低截止频率的高通可以消除这种影响。真正的模拟-合成器不会进入亚赫兹区域,所以你不会错过太多。

票数 5
EN

Stack Overflow用户

发布于 2010-04-28 19:10:14

一种快速产生带限波形的方法是使用带限步骤(BLEPs).您将生成带限制步骤本身:

并将其存储在一个可波形中,然后用一个带限步骤替换每个转换,以创建如下所示的波形:

请看带限声合成的穿行。

由于这种BLEP是非因果的(这意味着它延伸到未来),为了产生实时波形,最好使用最小相位带限步长,称为MinBLEP,它具有相同的频谱,但只扩展到过去:

MinBLEPs更进一步,采用加窗的正弦,执行最小相位重建,然后将结果集成到一个表中。现在,要制作一个振荡器,只需在波形中的每个不连续性处插入一个MinBLEP。所以对于方波,你插入一个波形倒置的MinBLEP,对于saw波,你插入一个值倒置的MinBLEP,但是你正常地生成斜坡。

票数 8
EN

Stack Overflow用户

发布于 2008-10-06 19:29:26

这就是我想到的,灵感来自尼尔斯的想法。把它贴在这里,以防对别人有用。我简单地用盒子过滤锯齿波,解析地使用最后一个样本的相位变化作为内核大小(或截止值)。这是相当好的工作,有一些可听到的别名在最高的音符,但对于正常使用,这听起来很棒。

为了更多地减少混叠,内核大小可以增加一点,例如,使2*相位变化听起来也不错,尽管你损失了一些最高频率。

此外,这里还有另一个很好的DSP资源,我在浏览SP时发现了类似的主题:ToolKit in C++ (STK)的合成。它是一个类库,有很多有用的DSP工具。它甚至已经准备好使用带限波形发生器。他们使用的方法是像我在第一篇文章中描述的那样集成sinc (虽然我猜他们比我做得更好……)。

代码语言:javascript
复制
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);
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/175312

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档