8 releases

0.2.12-alpha.0 Apr 7, 2023
0.2.11-alpha.0 Dec 19, 2022
0.2.5-alpha.0 Jun 21, 2022
0.2.4-alpha.0 Mar 14, 2022
0.1.42-alpha.0 Oct 27, 2021

#16 in #non-linear

22 downloads per month
Used in surge-synthesizer

GPL-3.0 license

155KB
2.5K SLoC

surgefx-shaper-clip crate description

Clipper

The Clipper module is a non-linear processing stage that can be used to introduce distortion and non-linearities into an audio signal. The clipper works by setting a threshold value, and any signal above that threshold is clipped or "hard-limited" to the threshold value. This creates a flattened, "squared off" waveform, which can introduce new harmonic content and other distortions into the signal.

Shape

The shape parameter in the Clipper module controls the shape of the clipper's threshold. The threshold can be a hard clip, a soft clip, or somewhere in between. A hard clip creates a sharp, rectangular waveform, while a soft clip creates a smoother, more rounded waveform. The shape parameter can be used to adjust the amount of distortion introduced by the clipper, as well as the tonal characteristics of the resulting waveform.

Smoke

The smoke module is a simple smoke test for the Clipper module. It generates a test signal, applies the Clipper module with default parameters, and outputs the resulting signal. This can be used to quickly test the functionality of the Clipper module, as well as to explore the different tonal characteristics that can be achieved with different shape parameters.

Overall, the Clipper module is a non-linear processing stage that can introduce distortion and non-linearities into an audio signal. Its mathematical properties can be described using tools such as Fourier analysis and signal processing theory, which allow us to understand and analyze its behavior in different contexts. The shape parameter can be used to adjust the amount and character of the distortion introduced by the clipper, while the smoke module provides a simple way to test and explore the module's functionality.

59> what distinguishes this from AsymShaper?

The Clipper module and the AsymShaper module are both non-linear processing stages that can introduce distortion and non-linearities into an audio signal. However, the main difference between them is in how they apply the distortion.

The Clipper module applies a hard clip to the signal, which means that any signal above the threshold value is clipped or "hard-limited" to the threshold value. This creates a flattened, "squared off" waveform.

The AsymShaper module, on the other hand, applies a more gradual shaping to the signal, which can create a more natural-sounding distortion. The AsymShaper uses a soft-clipping function that has a gradual transition into and out of the clipping region, which can produce more harmonically-rich distortion than a hard clip.

So, while both modules can be used for distortion, the Clipper module is more suited for hard-clipping and creating a "squared off" waveform, while the AsymShaper module is more suited for creating a more gradual and natural-sounding distortion.

60> how can we improve the Clipper?

There are several ways to improve the performance and sound quality of a clipper module:

  1. Implement higher-order clipping: Instead of simply clipping the signal at a fixed threshold, higher-order clipping applies a more complex distortion that can create more interesting harmonics and a richer sound. One example of higher-order clipping is the diode clipper, which uses a non-linear diode to create a more complex distortion.

  2. Implement variable threshold: Instead of using a fixed threshold, a variable threshold can be used that adapts to the amplitude of the input signal. This can help to preserve the dynamic range of the input signal and avoid excessive distortion.

  3. Implement oversampling: Oversampling involves increasing the sample rate of the input signal before applying the clipper and then downsampling the signal back to the original sample rate. This can reduce the distortion and improve the sound quality, especially at high levels of clipping.

  4. Implement anti-aliasing filtering: When downsampling the signal back to the original sample rate, anti-aliasing filtering can be applied to remove any high-frequency artifacts that may have been introduced during the oversampling process. This can further improve the sound quality of the clipped signal.

  5. Implement a soft-knee option: A soft-knee option can be added to the clipper, which creates a more gradual transition into and out of the clipping region. This can help to create a more natural-sounding distortion.

By incorporating these techniques, the performance and sound quality of a clipper module can be greatly improved, resulting in a more versatile and useful tool for audio processing.

61> could you show me a block diagram?

Sure! Here's a block diagram for a simple clipper:

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

The input signal is fed into the clipper function, which applies a distortion that clips the signal at a fixed threshold (determined by the "shape" parameter). The resulting output signal is the clipped version of the input signal.

62> could you show me some python code to visualize its behavior across a spectrum of parameter values?

Sure! Here's an example of how you can implement a simple clipper function in Python and plot its output for different threshold values (i.e., the "shape" parameter):

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()

This code generates a test signal (a sine wave), applies the clipper function to it for different threshold values (specified by the "shapes" list), and plots the input and output signals for visualization. You can modify the code to use your own input signal and adjust the threshold values to see how the clipper affects the signal.

63> how do we compensate for the power loss which occurs as a result of this clipping?

One way to compensate for the power loss that occurs as a result of clipping is to apply a gain boost after the clipping operation. This is because clipping can effectively reduce the overall level of the signal, which can make it appear quieter or less present in a mix. By applying a gain boost after the clipping, we can bring the overall level of the signal back up to a desired level.

However, it's important to note that applying too much gain can also result in additional distortion or saturation, especially if the signal is already close to or at full scale. So it's important to use careful judgement when applying gain boosts after clipping, and to always monitor the signal for unwanted distortion or artifacts.

64> can this gain boost be adaptive, based on the measured amount of clipping? does matching the power result in an increase in overall signal amplitude of the clipped signal?

Yes, the gain boost can be adaptive and based on the amount of clipping that occurs. One way to implement this is to use a feedback loop that measures the amount of clipping in the output signal and adjusts the gain accordingly. For example, if the output signal is clipping too much, the gain can be reduced, and if the output signal is not clipping enough, the gain can be increased.

Matching the power after clipping does not necessarily result in an increase in overall signal amplitude of the clipped signal. Rather, it ensures that the clipped signal has the same average power as the original unclipped signal. This can be useful for ensuring that the clipped signal has a similar perceived loudness as the unclipped signal, even though the clipped signal may have a reduced peak level.

65> could you show me an example of the updated circuit, using the feedback loop?

Certainly! Here is an example of an updated clipping circuit that includes a feedback loop to adjust the gain based on the amount of clipping:

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

In this circuit, V_in is the input signal and V_out is the clipped outputd signal. The clipping is performed by diode D1, which limits the voltage swing of the signal to a predetermined threshold. R1 and R2 are resistors that set the threshold for the clipping. R3 is a feedback resistor that sends a portion of the clipped signal back to an operational amplifier (op amp), which is used to apply a gain boost.

The op amp has a feedback loop that measures the amount of clipping in the output signal and adjusts the gain accordingly. This can be done using a sidechain circuit that measures the amplitude of the clipped signal and sends a control voltage to the op amp to adjust the gain. Alternatively, the feedback loop can be implemented using an automatic gain control (AGC) circuit that measures the peak level of the clipped signal and adjusts the gain accordingly.

The output of the op amp is V_out_gained, which is the clipped output signal with a gain boost applied. The gain can be adjusted based on the amount of clipping that occurs, so that the overall level of the signal is maintained even if clipping occurs. This helps to ensure that the clipped signal has a similar perceived loudness as the unclipped signal.

66> could you show me how to implement higher order clipping?

Sure! Higher order clipping can be implemented by cascading multiple clipping stages in series. Each stage can be a simple clipper with a different clipping threshold. The output of each stage is then fed into the next stage as input.

Here's an example Python code snippet that implements a 3-stage clipper:

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

This clip function takes in an input signal x, a shaping amount shape, and a list of clipping thresholds thresholds for each stage. It applies the first stage of clipping to the input signal using the first threshold, and then applies shaping to the clipped output. The resulting signal is then passed into the second stage of clipping using the second threshold, and the process is repeated for the third stage.

Here's an example usage of the function with three stages of clipping and different 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()

In this example, we apply three stages of clipping with thresholds of 0.2, 0.4, and 0.6. We also set the shaping amount to 2.0. The resulting clipped output signal is plotted alongside the input signal.

67> could you show me how to implement variable threshold?

Sure, here's an example implementation of a clipper with a variable threshold in Python:

import numpy as np

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

In this implementation, the threshold parameter controls the maximum value that the input signal x can take before it is clipped. By varying the value of threshold, we can adjust the amount of clipping that occurs.

For example, to apply variable threshold clipping to an input signal signal with a range of -1 to 1 and a default threshold of 0.5, we could use the following code:

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()

In this example, the input signal signal is generated using NumPy's random.uniform function, which creates an array of random numbers between -1 and 1. The clipper function is then used to apply variable threshold clipping to the signal, with the default threshold set to 0.5. The resulting clipped signal is stored in output_signal.

Finally, the input and output signals are plotted using the Matplotlib library, allowing us to visualize the effect of the variable threshold clipping.

68> could you show me how to implement oversampling?

Sure, oversampling is a technique used to reduce the impact of aliasing distortion in digital signal processing by sampling the signal at a higher rate than the original signal. Here's an example implementation of oversampling in Python using linear interpolation:

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

This function takes in an input signal x and a factor factor, which specifies the degree of oversampling. The signal is upsampled by factor, and then a linear interpolator is created to resample the signal back to its original rate. This process helps to reduce aliasing distortion by effectively increasing the resolution of the digital signal.

Note that oversampling can increase the computational load of a DSP algorithm, so it's important to consider the tradeoff between reducing distortion and increasing processing requirements.

69> could you show me how to implement anti-aliasing filtering?

Sure! Anti-aliasing filtering is a crucial step in digital signal processing to remove high frequency content from a signal before downsampling it. This is important to prevent aliasing, which can cause unwanted artifacts in the downsampled signal. Here's an example of how you can implement anti-aliasing filtering in Python using the scipy library:

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

Here, x is the input signal, fs is the sampling rate of the signal, and cutoff_freq is the frequency at which you want to cut off the high frequencies. The butter function is used to design a fourth-order Butterworth low-pass filter with the given cutoff frequency. The filtfilt function is used to apply the filter to the input signal using forward-backward filtering, which ensures zero phase distortion.

You can use this function to filter the output of the clipper or any other signal that you want to downsample without introducing aliasing artifacts.

70> could you show me how to implement a soft-knee option?

Sure! Here's an example implementation of a soft-knee option in the clipper function:

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

The softness parameter controls the steepness of the transition between the linear and nonlinear regions of the clipper. A value of 0 results in a hard clipper, while larger values result in a more gradual transition.

The calculation of the threshold value uses the logarithmic function and the sigmoid function to achieve a smooth transition between the linear and nonlinear regions.

Note that this implementation assumes that the input signal is normalized to the range [-1, 1]. If the signal is not normalized, the threshold and gain parameters should be adjusted accordingly.

71> what else can you teach about the clipping operation? how does it work mathematically? what does clipping do to the frequency spectrum?

Sure, I can explain more about clipping and its mathematical properties.

Clipping is a nonlinear distortion effect that limits the amplitude of a signal to a certain level. It is often used to add warmth and character to audio signals, particularly in guitar and bass amplifiers. When a signal exceeds the threshold of the clipping function, the waveform is "clipped" or "flattened," resulting in a distortion of the original signal.

Mathematically, clipping can be represented as a function that applies a limit or threshold to the input signal. The output of the function is the input signal if it is below the threshold, and the threshold value if the input signal is above it. The simplest form of a clipping function is a hard clipper, which is a function that sets all input values below a certain threshold to that threshold value, and all input values above the threshold to the threshold value with the opposite sign.

In the frequency domain, clipping causes harmonic distortion of the input signal. As the input signal is clipped, higher order harmonics are created, which can lead to an increase in overall harmonic content. The frequency spectrum of the clipped signal is essentially a sum of the original signal's harmonics and the harmonics created by the clipping process.

In addition to the hard clipper, there are other types of clipping functions that produce different effects, such as soft clipping, asymmetrical clipping, and variable threshold clipping. Soft clipping functions, for example, apply a gradual limiting to the input signal, which results in a smoother distortion effect than a hard clipper. Asymmetrical clipping functions, on the other hand, apply different thresholds to positive and negative portions of the input signal, resulting in a more complex waveform. Variable threshold clipping functions allow for adaptive clipping, where the threshold is adjusted based on the input signal's level.

Overall, clipping is a powerful and versatile distortion effect that can be used to add character and warmth to audio signals. Its mathematical properties can be analyzed to understand its effects on the frequency spectrum, and its implementation can be improved with techniques such as oversampling, anti-aliasing filtering, and soft-knee options.

72> is it typically implemented on analog hardware?

Clipping can be implemented on both analog and digital hardware. In the early days of audio recording and processing, clipping was often performed unintentionally due to the limited headroom of analog equipment. As digital audio technology has advanced, clipping can now be intentionally introduced through digital processing. In fact, many software plugins and digital audio workstations (DAWs) include various forms of clipping algorithms as a standard feature. However, there are still many analog hardware devices that use clipping for various purposes, such as guitar overdrive pedals, tube guitar amplifiers, and analog tape distortion.

73> can you show an example of a clipper in verilog-a?

Certainly! Here's an example of a simple clipper implemented in Verilog-A:

`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

This clipper takes an input voltage vin, compares it to two threshold voltages Vcl and Vch, and outputs the clipped voltage vout. The vth parameter can be used to adjust the threshold voltages dynamically.

Note that this example uses Verilog-A, a hardware description language used for analog and mixed-signal simulations. It is typically used in the design of integrated circuits, but can also be used to simulate and model analog circuits.

Dependencies

~13–22MB
~316K SLoC