带有合唱效果的圆形缓冲区环绕

信息处理 声音的 C
2022-02-20 15:24:01

我正在开发嵌入式 DSP 音频处理器并尝试实现合唱效果。据我了解,合唱效果是原始信号的多个延迟版本,其中延迟本身是随机的。在我的程序中,我将延迟设置为在初始化时随机定义,然后在程序执行期间保持静态。

我面临的挑战是,在使用循环缓冲区存储我过去的信号时,我不能简单地将延迟的信号包裹回循环缓冲区,因为存在多个延迟。

通过一个延迟,我可以简单地检查所需的延迟是否超过了循环缓冲区中的当前位置,如果是,我可以跳到循环缓冲区的末尾减去延迟并添加我在循环缓冲区中的当前位置。

if (Delay > Current_Position_in_Buffer)
{
    Circular_Array[Length_of_Array + Current_Position_in_Buffer - Delay];
}
else
{
    Circular_Array[Current_Position_in_Buffer - Delay];
}

有没有一种有效的方法来检查和调整每个延迟信号在我的循环数组中的位置,而无需使用 if 语句单独检查每个延迟?

4个回答

如果我正确理解您的要求,那么这可能更像是一个编程问题......但解决方案是使用模运算来计算延迟的位置。长度等于 2 的幂,这可以通过 AND 指令来完成。

const int Mask = (1 << 16) - 1;
Circular_Array[(Current_Position_in_Buffer - Delay) & Mask];

在此示例中,缓冲区的长度为216.

更一般地,您可以使用模运算符,但它会更慢。

在许多 DSP 中,你会得到一个可编程的地址计算单元——我认为这就是摩托罗拉 56k DSP 系列(以前)流行的主要原因。您通常可以只编写程序来为您执行“环绕索引”,而您的代码不需要检查任何地址。

现在,您使用 C 语言编写此代码的事实表明您使用了一些具有成熟编译器支持的处理器平台。

在这种情况下,您可能根本不应该担心——内存访问通常比分支慢得多(if语句通常编译成的),所以这可能根本没有区别——尤其是因为许多平台(ARM、x86、SPARC (我认为),...)确实有条件移动,这基本上意味着内存的加载可能在管道的早期就开始了。

当啤酒花回答时,我将建议使用 AND 掩码的两个长度的幂。正如 MM 建议的那样,内存获取时间可能会淹没这样的轻微优化。一般情况下的模将是一个更糟糕的解决方案,因为它带来了除法运算。对于一般情况,您仍然需要一个 if 语句,但是您可以通过如下编码来稍微改进它(微不足道?):

索引 = Current_Position_in_Buffer - 延迟;
if( Index < 0 ) Index += Length_of_Array;

Circular_Array[索引];

也许值两美分。

赛德

根据可用内存、处理器缓存大小和分支预测性能(等),将数据写入循环缓冲区两次、写入循环缓冲区以及直接写入相同大小的内存块可能是手动优化在循环缓冲区之前。这样,在初始预填充之后,在从当前索引中减去延迟后,内存中总会有有效数据,并且不需要每个偏移量的模回绕来读取这些偏移量样本。

在某些系统上,可以将循环缓冲区内存映射到该缓冲区之前的内存地址块,因此只需执行一次数据写入,数据就会出现在非模偏移读取所需的两个内存地址成为可能。