#信号处理 #音频处理 #模块 #阈值 #合成器 #削波器 #非线性

surgeshaper-clip

surge合成器 -- 削波波形整形器

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日

#5 in #削波器


surge合成器中使用

GPL-3.0许可

155KB
2.5K SLoC

surgefx-shaper-clip包描述

削波器

Clipper模块是一个非线性处理阶段,可用于向音频信号中引入失真和非线性。削波器通过设置阈值值来工作,任何高于该阈值的信号都会被削波或“硬限制”到阈值值。这会创建一个平坦的、“平方”波形,可以向信号中引入新的谐波内容和其他失真。

形状

Clipper模块中的shape参数控制削波器阈值的形状。阈值可以是硬削波、软削波或两者之间。硬削波会创建一个尖锐的矩形波形,而软削波会创建一个更平滑、更圆滑的波形。shape参数可用于调整削波器引入的失真量以及最终波形的音调特性。

烟雾

smoke模块是对Clipper模块的简单烟雾测试。它生成一个测试信号,使用默认参数应用Clipper模块,并输出结果信号。这可以用于快速测试Clipper模块的功能,以及探索使用不同的shape参数可以达到的不同音调特性。

总的来说,Clipper模块是一个非线性处理阶段,可以将失真和非线性引入音频信号。其数学属性可以使用傅里叶分析和信号处理理论等工具来描述,这些工具使我们能够理解并分析其在不同环境中的行为。shape参数可用于调整削波器引入的失真量和特性,而smoke模块提供了一个简单的方法来测试和探索模块的功能。

59> 这与AsymShaper有什么区别?

Clipper模块和AsymShaper模块都是非线性处理阶段,可以将失真和非线性引入音频信号。然而,它们之间的主要区别在于它们应用失真的方式。

Clipper 模块对信号应用硬裁剪,这意味着任何高于阈值值的信号都会被裁剪或“硬限制”到阈值值。这会产生一个平坦的、“方波形”的波形。

另一方面,AsymShaper 模块对信号应用更渐进的形状,这可以创建更自然的失真效果。《AsymShaper》使用一个具有渐进过渡进入和退出裁剪区域的软裁剪函数,这可以产生比硬裁剪更丰富的谐波失真。

因此,虽然这两个模块都可以用于失真,但《Clipper》模块更适合硬裁剪和创建“方波形”,而《AsymShaper》模块更适合创建更渐进和自然的声音失真。

60> 我们如何改进Clipper呢?

有几种方法可以改进裁剪模块的性能和音质

  1. 实现高阶裁剪:高阶裁剪不是简单地在一个固定的阈值处裁剪信号,而是一种更复杂的失真,可以产生更有趣的谐波和更丰富的音质。高阶裁剪的一个例子是二极管裁剪,它使用非线性二极管来创建更复杂的失真。

  2. 实现可变阈值:使用可变阈值而不是固定阈值,该阈值可以适应输入信号的幅度。这有助于保留输入信号的动态范围并避免过度失真。

  3. 实现过采样:过采样涉及在应用裁剪之前增加输入信号的采样率,然后下采样信号回到原始采样率。这可以减少失真并提高音质,尤其是在高裁剪水平时。

  4. 实现抗混叠滤波:在下采样信号回到原始采样率时,可以应用抗混叠滤波以去除在过采样过程中可能引入的任何高频杂波。这可以进一步提高裁剪信号的音质。

  5. 实现软膝选项:可以在裁剪器中添加软膝选项,这会在进入和退出裁剪区域时创建更渐进的过渡。这有助于创建更自然的失真。

通过结合这些技术,可以大大提高裁剪模块的性能和音质,使其成为音频处理中更通用、更有用的工具。

61> 你能给我展示一个框图吗?

当然可以!下面是一个简单裁剪器的框图

       +-------------------+
       |                   |
       |    Input signal   |
       |                   |
       +-------------------+
                    |
                    |
                    v
         +------------------------+
         |                        |
         |  Clipper function      |
         |  (threshold = shape)  |
         |                        |
         +------------------------+
                    |
                    |
                    v
       +-------------------+
       |                   |
       |   Output signal   |
       |                   |
       +-------------------+

输入信号被馈送到裁剪函数中,该函数应用一个失真,将信号裁剪到一个固定的阈值(由“形状”参数确定)。产生的输出信号是输入信号的裁剪版本。

62> 你能给我一些Python代码来可视化其在一系列参数值上的行为吗?

当然可以!下面是一个示例,说明如何在Python中实现一个简单的裁剪函数并绘制其输出,用于不同的阈值值(即“形状”参数)

import numpy as np
import matplotlib.pyplot as plt

def clipper(x, shape):
    return np.clip(x, -shape, shape)

# Generate a test signal (sine wave)
fs = 44100  # Sample rate (Hz)
dur = 1.0   # Duration (seconds)
f0 = 1000   # Frequency (Hz)
t = np.linspace(0, dur, int(fs*dur))
x = np.sin(2*np.pi*f0*t)

# Plot the input signal
plt.figure(figsize=(10,4))
plt.plot(t, x)
plt.title('Input signal')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude')

# Plot the output signals for different threshold values
shapes = [0.1, 0.3, 0.5, 0.7, 0.9]  # Threshold values
plt.figure(figsize=(10,4))
plt.plot(t, x, label='Input signal')
for shape in shapes:
    y = clipper(x, shape)
    plt.plot(t, y, label=f'shape={shape}')
plt.title('Output signals for different threshold values')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude')
plt.legend()
plt.show()

此代码生成一个测试信号(正弦波),将裁剪函数应用于它,对于不同的阈值值(指定为“shapes”列表),并绘制输入和输出信号以进行可视化。您可以将代码修改为使用自己的输入信号并调整阈值值,以查看裁剪器如何影响信号。

63> 我们如何补偿由此裁剪引起的功率损失呢?

补偿由于削波而产生的功率损失的一种方法是在削波操作后应用增益提升。这是因为削波可以有效地降低信号的总体电平,使其听起来更安静或在混音中更不明显。通过在削波后应用增益提升,我们可以将信号的总体电平提升到期望的水平。

然而,需要注意的是,应用过多的增益也可能导致额外的失真或饱和,尤其是在信号已经接近或达到全量程的情况下。因此,在削波后应用增益提升时,应谨慎判断,并始终监测信号以避免不必要的失真或伪迹。

64>这种增益提升能否根据测量的削波量进行调整?匹配功率会导致削波信号的总体振幅增加吗?

是的,增益提升可以是自适应的,基于发生的削波量。实现这种功能的一种方法是通过使用反馈环来测量输出信号中的削波量,并相应地调整增益。例如,如果输出信号削波过多,则可以降低增益;如果输出信号削波不足,则可以增加增益。

削波后的功率匹配不一定导致削波信号的总体振幅增加。相反,它确保了削波信号具有与原始未削波信号相同的平均功率。这有助于确保削波信号具有与未削波信号相似的感知响度,尽管削波信号的峰值电平可能较低。

65>你能给我一个使用反馈环的更新电路的例子吗?

当然可以!以下是一个包含根据削波量调整增益的反馈环的更新削波电路的例子。

       +---[ R1 ]---+
       |            |
  V_in +---[ D1 ]---+---> V_out
       |            |
       +---[ R2 ]---+
           |
           R3
           |
           +-------[ Op Amp ]--------> V_out_gained

在这个电路中,V_in是输入信号,V_out是削波输出信号。削波是通过二极管D1实现的,它限制信号的电压摆幅到一个预定的阈值。R1R2是电阻,用于设置削波的阈值。R3是一个反馈电阻,将部分削波信号送回到运算放大器(op amp),用于应用增益提升。

运算放大器有一个反馈环,它测量输出信号中的削波量,并相应地调整增益。这可以通过测量削波信号幅度的旁路电路来完成,并将控制电压发送到运算放大器以调整增益。或者,反馈环可以使用测量削波信号峰值电平的自动增益控制(AGC)电路来实现。

运算放大器的输出是V_out_gained,这是应用了增益提升的削波输出信号。增益可以根据削波量进行调整,以确保即使在削波发生的情况下,信号的总体电平也能保持,这有助于确保削波信号具有与未削波信号相似的感知响度。

66>你能给我展示如何实现高阶削波吗?

当然可以!通过串联多个削波级可以实现高阶削波。每个级可以是一个具有不同削波阈值的简单削波器。每个级的输出然后作为下一级的输入。

以下是一个实现3级削波的Python代码片段的例子。

import numpy as np

def clip(x, shape, thresholds):
    """Applies multiple stages of clipping to input signal x.

    Args:
        x (np.ndarray): Input signal to clip.
        shape (float): Amount of shaping to apply to signal.
        thresholds (list): List of clipping thresholds for each stage.

    Returns:
        np.ndarray: Clipped output signal.
    """
    y = x.copy()
    for threshold in thresholds:
        y = np.clip(y * (1 / threshold), -1, 1) * threshold
        y = np.tanh(y * shape)
    return y

clip函数接收一个输入信号x、一个整形量shape以及每个阶段的裁剪阈值列表thresholds。它使用第一个阈值对输入信号进行第一次裁剪,然后对裁剪后的输出进行整形。然后,将生成的信号通过第二个阈值传递到第二个裁剪阶段,重复此过程进行第三个阶段。

以下是一个使用三个裁剪阶段和不同阈值的函数示例用法

import matplotlib.pyplot as plt

# Create a test signal
t = np.linspace(0, 1, 44100, endpoint=False)
x = np.sin(2 * np.pi * 440 * t)

# Apply clipping
thresholds = [0.2, 0.4, 0.6]
y = clip(x, 2.0, thresholds)

# Plot the input and output signals
plt.plot(td, x, label='Input')
plt.plot(t, y, label='Output')
plt.legend()
plt.show()

在此示例中,我们应用了三个阶段的裁剪,阈值分别为0.2、0.4和0.6。我们还设置了整形量为2.0。生成的裁剪输出信号与输入信号一起绘制。

67> 你能展示如何实现可变阈值吗?

当然,以下是一个使用Python实现的具有可变阈值的裁剪器的示例

import numpy as np

def clipper(x, threshold):
    return np.clip(x, -threshold, threshold)

在此实现中,threshold参数控制输入信号x在裁剪之前可以接受的最高值。通过改变threshold的值,我们可以调整发生的裁剪量。

例如,要对范围为-1到1的输入信号signal应用可变阈值裁剪,默认阈值为0.5,可以使用以下代码

import numpy as np

def clipper(x, threshold):
    return np.clip(x, -threshold, threshold)

signal = np.random.uniform(-1, 1, size=44100) # Generate a random signal
threshold = 0.5 # Set the default threshold

# Apply variable threshold clipping to the signal
output_signal = clipper(signal, threshold)

# Plot the input and output signals
import matplotlib.pyplot as plt
plt.plot(signal)
plt.plot(output_signal)
plt.show()

在此示例中,输入信号signal使用NumPy的random.uniform函数生成,该函数创建一个介于-1和1之间的随机数数组。然后使用clipper函数将可变阈值裁剪应用于信号,默认阈值为0.5。生成的裁剪信号存储在output_signal中。

最后,使用Matplotlib库绘制输入和输出信号,使我们能够可视化可变阈值裁剪的效果。

68> 你能展示如何实现过采样吗?

当然,过采样是一种用于通过以高于原始信号速率采样信号来减少数字信号处理中混叠失真影响的技术。以下是一个使用Python中线性插值实现的过采样示例

import numpy as np
from scipy.interpolate import interp1d

def oversample(x, factor):
    # Upsample by factor
    upsampled = np.zeros(len(x) * factor)
    upsampled[::factor] = x

    # Create linear interpolator
    interp = interp1d(np.arange(0, len(upsampled)), upsampled, kind='linear')

    # Resample at original rate
    resampled = interp(np.linspace(0, len(upsampled) - 1, len(x)))

    return resampled

此函数接收输入信号x和一个因子factor,该因子指定过采样的程度。信号通过factor进行上采样,然后创建一个线性插值器将信号重新采样回原始速率。此过程通过有效地增加数字信号的分辨率来减少混叠失真。

请注意,过采样可能会增加DSP算法的计算负载,因此需要在减少失真和增加处理要求之间权衡。

69> 你能展示如何实现抗混叠滤波吗?

当然!抗混叠滤波是数字信号处理中的关键步骤,用于在降采样之前从信号中去除高频内容。这对于防止混叠很重要,混叠可能会在降采样信号中引起不希望的伪影。以下是在Python中使用scipy库实现抗混叠滤波的示例

import numpy as np
from scipy.signal import butter, filtfilt

def anti_aliasing_filter(x, fs, cutoff_freq):
    # Design a Butterworth low-pass filter with the given cutoff frequency
    nyquist_freq = fs / 2
    normalized_cutoff_freq = cutoff_freq / nyquist_freq
    b, a = butter(4, normalized_cutoff_freq, 'lowpass')

    # Apply the filter to the input signal using forward-backward filtering
    y = filtfilt(b, a, x)
    return y

在此,x是输入信号,fs是信号的采样率,而cutoff_freq是你想切断高频的频率。使用butter函数设计了一个截止频率为给定值的四阶巴特沃斯低通滤波器。使用filtfilt函数通过前向-后向滤波将滤波器应用于输入信号,这确保了零相位失真。

你可以使用此函数来过滤裁剪器或其他任何你想要降采样而不引入混叠伪影的信号。

70> 你能展示如何实现软膝选项吗?

当然可以!这是一个在clipper函数中实现软膝盖选项的示例实现

import numpy as np

def clipper(x, shape, softness=0.1):
    abs_x = np.abs(x)
    sign_x = np.sign(x)

    if softness == 0:
        threshold = shape
    else:
        threshold = np.log(1 + np.exp(shape)) / np.log(1 + np.exp(shape * softness))

    y = np.zeros_like(x)
    indices = abs_x > threshold
    y[indices] = sign_x[indices] * (threshold + (abs_x[indices] - threshold) / (1 + np.exp(-shape * softness)))
    y[~indices] = x[~indices]

    return y

softness参数控制clipper的线性区域和非线性区域之间的过渡陡峭程度。值为0表示硬clipper,而较大的值会导致更平缓的过渡。

阈值值的计算使用了对数函数和Sigmoid函数,以实现线性区域和非线性区域之间的平滑过渡。

请注意,此实现假设输入信号已归一化到[-1, 1]的范围。如果信号未归一化,则应相应调整阈值和增益参数。

71> 你还可以教我关于剪辑操作的其他内容吗?它在数学上是如何工作的?剪辑对频谱有什么影响?

当然,我可以更多地解释关于剪辑及其数学属性的内容。

剪辑是一种非线性失真效果,它限制信号的幅度在一定水平。它常用于为音频信号添加温暖和特性,特别是在吉他和大提琴放大器中。当信号超过剪辑函数的阈值时,波形会被“剪辑”或“压平”,从而导致原始信号的失真。

从数学的角度来看,剪辑可以表示为一个对输入信号应用限制或阈值的函数。如果输入信号低于阈值,则函数的输出是输入信号;如果输入信号高于阈值,则输出是阈值值。剪辑函数的最简单形式是硬剪辑,这是一种将所有低于一定阈值的输入值设置为该阈值值,并将所有高于阈值的输入值设置为与阈值值相反符号的函数。

在频域中,剪辑会导致输入信号的谐波失真。随着输入信号的剪辑,会创建更高阶的谐波,这可能导致整体谐波内容的增加。剪辑信号的频谱基本上是原始信号谐波和由剪辑过程创建的谐波的总和。

除了硬剪辑之外,还有其他类型的剪辑函数会产生不同的效果,例如软剪辑、非对称剪辑和可变阈值剪辑。例如,软剪辑函数会对输入信号应用渐进式限制,这比硬剪辑产生更平滑的失真效果。另一方面,非对称剪辑函数对输入信号的正面和负面部分应用不同的阈值,从而产生更复杂的波形。可变阈值剪辑函数允许自适应剪辑,其中阈值根据输入信号的水平进行调整。

总的来说,剪辑是一种强大且通用的失真效果,可以用于为音频信号添加特性和温暖。可以通过分析其数学属性来了解其对频谱的影响,并通过过采样、抗混叠滤波和软膝盖选项等技术改进其实现。

72> 它通常在模拟硬件上实现吗?

剪辑可以在模拟和数字硬件上实现。在音频录制和处理的早期,由于模拟设备的动态范围有限,剪辑往往是不经意间发生的。随着数字音频技术的进步,现在可以通过数字处理有意引入剪辑。事实上,许多软件插件和数字音频工作站(DAW)包括各种剪辑算法作为标准功能。然而,仍有许多模拟硬件设备使用剪辑,例如吉他过载踏板、电子管吉他放大器和模拟磁带失真。

73> 你能展示一个在Verilog-A中的clipper示例吗?

当然!下面是使用Verilog-A实现的简单clipper的示例

`include "disciplines.vams"

module clipper_va (vin, vout, vth);

// Define electrical terminals
electrical vin, vout;
electrical vth;

// Define parameters
parameter real Vcl = 1.0;
parameter real Vch = -1.0;

// Define internal variables
real v;

// Calculate the output voltage
analog begin
  v = vin.v;

  if (v > Vcl) begin
    vout.v = Vcl;
  end else if (v < Vch) begin
    vout.v = Vch;
  end else begin
    vout.v = v;
  end
end

endmodule

该剪刀取输入电压 vin,将其与两个阈值电压 VclVch 进行比较,并输出剪辑后的电压 vout。可以使用 vth 参数动态调整阈值电压。

请注意,此示例使用 Verilog-A,这是一种用于模拟电路和混合信号模拟的硬件描述语言。它通常用于集成电路的设计,但也可以用于模拟和建模模拟电路。

依赖关系

~12–20MB
~300K SLoC