8个版本

0.2.12-alpha.02023年4月7日
0.2.11-alpha.02022年12月19日
0.2.5-alpha.02022年6月21日
0.2.4-alpha.02022年3月14日
0.1.42-alpha.02021年10月27日

#失真中排名3

Download history 2/week @ 2024-03-17 21/week @ 2024-03-24 17/week @ 2024-03-31 207/week @ 2024-06-09 9/week @ 2024-06-16

216次每月下载
用于surge-synthesizer

GPL-3.0许可证

155KB
2.5K SLoC

DigiShaper

此代码实现了称为DigiShaper的波形整形算法。波形整形器将输入信号以非线性方式扭曲,产生具有不同音色或特性的新输出信号。在这种情况下,DigiShaper算法将输入信号映射到具有16个段的分段线性曲线上,每个段具有不同的斜率。扭曲量由“驱动”参数控制。

DigiShaper算法使用多个数学概念来实现这一结果。首先,它利用分段线性函数的概念,这些函数在不同的区间上由不同的线性方程定义。在这种情况下,DigiShaper使用16个线性方程来定义其分段线性曲线。

其次,该算法使用数字信号处理的概念,如采样率转换和量化。首先将输入信号按16倍缩放,然后将其量化为整数值,最后按16倍缩放回。这使得算法能够仅使用少量离散级别来实现分段线性曲线,从而降低算法的计算成本。

第三,该算法使用SIMD指令(SSE2)一次对多个数据点进行并行计算。这提高了算法的效率,使其适用于实时音频处理应用。

总的来说,DigiShaper算法是一种相对简单但有效的音频信号扭曲方法,它利用数字信号处理中的多个数学概念来实现其结果。

2>基本上,这归结为这个方程式,不是吗:(double)(int)((double)(x*0.0625f+1.0))*0.0625f;

是的,这是正确的。DigiShaper算法的主要逻辑在此方程式中实现,用于计算输入信号的单一样本的输出值

(double)((int)((double)(x*p0inv*16.f+1.0)))*p0*0.0625f;

以下是此方程式的分解

  • x是输入样本值。

  • p0是驱动参数,它控制对信号的扭曲量。

  • p0invp0 的倒数,它是预先计算的以提高效率。

  • x * p0inv * 16.f 通过一个16倍的系数缩放输入样本,然后乘以 p0inv

  • + 1.0 将缩放后的输入信号上移1.0,使得输出值范围在[1, 17]而不是[0, 16]。

  • (double)((int)(...)) 通过将值强制转换为整数数据类型将上移后的值向下取整到最近的整数,从而有效地量化了输入信号。

  • * p0 * 0.0625f 通过16倍的系数将量化的值再次缩放并乘以 p0,这将对信号应用失真。

因此,总的来说,这个方程将输入信号映射到一个由16段组成的分段线性曲线上,每段都有不同的斜率,而应用的失真量由 p0 参数控制。

3> 您能给我一个框图吗?

当然可以!下面是一个显示 DigiShaper 算法主要处理步骤的框图

                  +-------------+         +-------------+
Input signal ---> | Scale by 16 | ------> | Quantization| ------> +----------------+
                  +-------------+         +-------------+         | Piecewise-linear|
                                                                    | Curve Mapping  |
                  +-------------+         +-------------+         |                |
Drive parameter --> | Reciprocal  | ------> | Scale by 16 | ------> |                |
                  +-------------+         +-------------+         |                |
                                                                    |                |
                  +-------------+         +-------------+         |                |
Quantized value --> | Cast to int | ------> | Scale by p0 | ------> | Output Signal  |
                  +-------------+         +-------------+         +----------------+

如你所见,输入信号首先通过16倍系数进行缩放,然后量化为整数值,这实际上将其映射到一个由16段组成的分段线性曲线上。量化值然后通过16倍系数进行缩放并乘以驱动参数 p0,这将对信号应用失真。输出信号是这一处理过程的最终结果。p0 参数的倒数是预先计算的以提高效率,并且使用 SIMD 指令一次对多个数据点进行并行计算。

此代码实现了称为DigiShaper的波形整形算法。波形整形器将输入信号以非线性方式扭曲,产生具有不同音色或特性的新输出信号。在这种情况下,DigiShaper算法将输入信号映射到具有16个段的分段线性曲线上,每个段具有不同的斜率。扭曲量由“驱动”参数控制。

DigiShaper算法使用多个数学概念来实现这一结果。首先,它利用分段线性函数的概念,这些函数在不同的区间上由不同的线性方程定义。在这种情况下,DigiShaper使用16个线性方程来定义其分段线性曲线。

其次,该算法使用数字信号处理的概念,如采样率转换和量化。首先将输入信号按16倍缩放,然后将其量化为整数值,最后按16倍缩放回。这使得算法能够仅使用少量离散级别来实现分段线性曲线,从而降低算法的计算成本。

第三,该算法使用SIMD指令(SSE2)一次对多个数据点进行并行计算。这提高了算法的效率,使其适用于实时音频处理应用。

总的来说,DigiShaper算法是一种相对简单但有效的音频信号扭曲方法,它利用数字信号处理中的多个数学概念来实现其结果。

2>基本上,这归结为这个方程式,不是吗:(double)(int)((double)(x*0.0625f+1.0))*0.0625f;

是的,这是正确的。DigiShaper算法的主要逻辑在此方程式中实现,用于计算输入信号的单一样本的输出值

(double)((int)((double)(x*p0inv*16.f+1.0)))*p0*0.0625f;

以下是此方程式的分解

  • x是输入样本值。

  • p0是驱动参数,它控制对信号的扭曲量。

  • p0invp0 的倒数,它是预先计算的以提高效率。

  • x * p0inv * 16.f 通过一个16倍的系数缩放输入样本,然后乘以 p0inv

  • + 1.0 将缩放后的输入信号上移1.0,使得输出值范围在[1, 17]而不是[0, 16]。

  • (double)((int)(...)) 通过将值强制转换为整数数据类型将上移后的值向下取整到最近的整数,从而有效地量化了输入信号。

  • * p0 * 0.0625f 通过16倍的系数将量化的值再次缩放并乘以 p0,这将对信号应用失真。

因此,总的来说,这个方程将输入信号映射到一个由16段组成的分段线性曲线上,每段都有不同的斜率,而应用的失真量由 p0 参数控制。

3> 您能给我一个框图吗?

当然可以!下面是一个显示 DigiShaper 算法主要处理步骤的框图

                  +-------------+         +-------------+
Input signal ---> | Scale by 16 | ------> | Quantization| ------> +----------------+
                  +-------------+         +-------------+         | Piecewise-linear|
                                                                    | Curve Mapping  |
                  +-------------+         +-------------+         |                |
Drive parameter --> | Reciprocal  | ------> | Scale by 16 | ------> |                |
                  +-------------+         +-------------+         |                |
                                                                    |                |
                  +-------------+         +-------------+         |                |
Quantized value --> | Cast to int | ------> | Scale by p0 | ------> | Output Signal  |
                  +-------------+         +-------------+         +----------------+

如你所见,输入信号首先通过16倍系数进行缩放,然后量化为整数值,这实际上将其映射到一个由16段组成的分段线性曲线上。量化值然后通过16倍系数进行缩放并乘以驱动参数 p0,这将对信号应用失真。输出信号是这一处理过程的最终结果。p0 参数的倒数是预先计算的以提高效率,并且使用 SIMD 指令一次对多个数据点进行并行计算。

4> 我们如何改进这个算法?

有几个方法可以改进 DigiShaper 算法

  1. 使用高阶曲线:DigiShaper 算法使用一个由16段组成的分段线性曲线来映射输入信号。使用更高阶的曲线(例如二次或三次曲线)可以提供更灵活和丰富的失真效果。然而,这需要更多的计算和内存,因此对于某些应用可能不可行。

  2. 使用更复杂的量化方案:DigiShaper 算法使用简单的量化方案,将缩放的输入信号向下取整到最近的整数。使用更复杂的量化方案(例如四舍五入到最近的整数或使用抖动算法)可以提高曲线映射的精度并减少量化噪声。

  3. 添加过采样:过采样是一种涉及以高于输出采样率处理信号的技术,然后对结果进行下采样。这有助于减少混叠伪影并提高算法的整体音质。

  4. 使用非线性映射:DigiShaper 算法使用线性映射将输入信号映射到分段线性曲线上。使用非线性映射(例如 S 型函数或双曲正切函数)可以提供更复杂和有趣的失真效果。

  5. 实现软裁剪函数:DigiShaper 算法使用硬裁剪,这会产生刺耳和不愉快失真伪影。实现软裁剪函数(例如 tanh 函数)可以提供更平滑和更音乐的失真效果。

  6. 添加后滤波器:DigiShaper 算法不包括任何后处理滤波,这有助于塑造输出信号的频谱内容并减少噪声。添加后滤波器(例如低通或高通滤波器)可以提高算法的整体音质。

以下是一些改进DigiShaper算法的思路。最终选择哪种改进取决于具体的应用和所需的声音质量。

5> 你能否解释一下分段线性曲线映射的含义?我不太清楚这个方程是如何实现这一点的

当然可以!DigiShaper算法将输入信号映射到由16个分段线性曲线组成的曲线,每个曲线段有不同的斜率。这种映射的目的是通过选择性地放大或衰减信号的不同部分来引入信号失真。

DigiShaper算法中使用的方程通过将输入信号量化为16个离散级别之一来实现这种映射,这些级别对应于分段线性曲线的16个曲线段。曲线的每个段具有不同的斜率,这决定了在该段中对输入信号的放大或衰减程度。

以下是一个简化的例子来说明这是如何工作的。假设我们有一个由三个曲线段组成的分段线性曲线,每个曲线段具有不同的斜率

     /\
    /  \
   /    \
  /      \
 /________\

如果我们想将输入信号映射到这条曲线,我们可以首先将输入信号按3倍的比例缩放,使其覆盖与曲线相同的范围

   Input signal
        /\
       /  \
      /    \
     /      \
    /________\
  Curve range

接下来,我们将缩放后的输入信号量化为三个离散级别之一,这些级别对应于曲线的三个曲线段

    Level 1
        /\
       /  \
      /    \
     /      \
    /________\
    Level 2
        /\
       /  \
      /    \
     /      \
    /________\
    Level 3
        /\
       /  \
      /    \
     /      \
    /________\

最后,我们将量化级别对应的曲线段的斜率应用到输入信号上

    Level 1   Level 2   Level 3
        /\        /\        /\
       /  \      /  \      /  \
      /    \    /    \    /    \
     /      \  /      \  /      \
    /________\/________\/________\

在DigiShaper算法中,使用我们之前看过的方程来执行量化和斜率计算

(double)((int)((double)(x*p0inv*16.f+1.0)))*p0*0.0625f;

方程末尾乘以p0确定了曲线段的斜率,而量化是通过将结果强制转换为整数然后再转换为浮点数来实现的。

希望这能说明DigiShaper算法如何将输入信号映射到分段线性曲线上!

6> 这听起来不太对,感觉像是胡说八道

如果我的解释不清楚或令人困惑,请原谅。让我尝试用不同的方式再解释一遍。

DigiShaper算法通过将信号量化为16个离散级别之一,然后乘以一个斜率因子,将输入信号映射到分段线性曲线上。用于实现此映射的方程如下

(double)((int)((double)(x*p0inv*16.f+1.0)))*p0*0.0625f;

让我们一步一步地分解它

  1. 将输入信号按16倍和p0inv的比例缩放
x * p0inv * 16.f

这使输入信号覆盖范围为[0, 16],然后进一步通过p0inv进行缩放。这个p0inv因子决定了应用于信号的失真量。

  1. 将缩放后的输入信号加1.0
x * p0inv * 16.f + 1.0

这使缩放后的输入信号向上移动1.0,使其覆盖范围为[1, 17],而不是[0, 16]。

这样做是因为接下来的步骤涉及将信号量化为整数值,加1.0确保量化值在[1, 16]范围内,而不是[0, 15]。

  1. 将结果强制转换为整数,然后再将其转换回浮点数
(double)((int)(...))

通过将结果舍入到最接近的整数来量化移动后的输入信号,然后将结果整数转换回浮点数。

  1. 将量化值乘以p0和缩放因子
((double)((int)(...)))*p0*0.0625f

通过将量化值乘以p0和缩放因子(0.0625f)来应用失真到信号。缩放因子用于将量化值缩放回范围[0, 1]。

得到的结果是映射到具有16个曲线段、每个曲线段具有不同斜率(由p0确定)的分段线性曲线后的输出信号,每个曲线段具有不同的斜率。

我希望这个解释更清晰、更准确。如果您还有任何问题,请告诉我!

7> 哦,好的,那么这是一个16级量化函数

是的,没错!DigiShaper算法通过使用16段分段线性曲线,将输入信号有效量化到16个离散级别,每段的斜率由p0参数决定。量化是通过将偏移后的输入信号四舍五入到最近的整数,然后将其缩放回16倍并乘以p0来应用的。

8> 驱动函数是不是对输入信号进行量化的放大器...

是的,这样理解是正确的!p0参数(在DigiShaper算法中称为“驱动”参数)控制应用于输入信号的失真量。当p0设置为高值时,失真效果会更明显,而当p0设置为低值时,效果会更微妙。

p0参数充当放大器的作用,通过将量化值缩放回p0倍来放大输入信号到量化电路。这会增强信号幅度,并向信号中添加谐波内容,从而产生失真效果。

9> 你在上面画的是那些三角形是什么意思?

如果三角图示令您感到困惑,请谅解!它们旨在说明DigiShaper算法将输入信号映射到的分段线性曲线。

在分段线性曲线中,曲线由一系列具有不同斜率的线段定义,如下所示

     /\
    /  \
   /    \
  /      \
 /________\

曲线的每一段都有不同的斜率,这决定了输入信号在该段中被放大或衰减的程度。

三角图示旨在表示这种曲线,每个三角形代表曲线的一个线段。三角形的大小和斜率将与分段线性曲线中线段的大小和斜率相对应。

我希望这澄清了三角图示所代表的内容!

10> 谢谢!你能展示如何在SystemVerilog中实现一个量化电路吗?

当然可以!这是一个简单的量化电路在SystemVerilog中的实现示例

module quantizer(input logic signed [15:0] in, input logic [3:0] levels,
                 output logic signed [15:0] out);

  // Quantize the input signal to one of the discrete levels
  always_comb begin
    case (in)
      0:     out = levels[0];
      1:     out = levels[1];
      2:     out = levels[2];
      3:     out = levels[3];
      4:     out = levels[4];
      5:     out = levels[5];
      6:     out = levels[6];
      7:     out = levels[7];
      8:     out = levels[8];
      9:     out = levels[9];
      10:    out = levels[10];
      11:    out = levels[11];
      12:    out = levels[12];
      13:    out = levels[13];
      14:    out = levels[14];
      default: out = levels[15];
    endcase
  end

endmodule

在这个实现中,in输入是一个表示要量化输入信号的16位有符号信号。levels输入是一个表示输入信号可以量化的16个离散级别的4位信号。out输出是一个表示量化输出信号的有符号16位信号。

量化过程是通过一个检查输入信号值的case语句实现的,并将相应的级别分配给输出信号。默认情况将最高级别分配给任何超出定义级别范围的输入值。

请注意,这是一个非常基础的量化电路实现,它只支持固定级别的量化。在实际应用中,可能需要更复杂的量化方案(如四舍五入到最近的整数或使用抖动算法)以达到所需的精度并减少量化噪声。

依赖项

~12–21MB
~299K SLoC