Building-an-Analog-Compressor-Part-1

1. Introduction

Welcome Back!

Hello again, and welcome to the second part of our exciting journey into building an analog compressor and transforming it into a VST plugin. In the previous installment, Part 0, we laid the foundation by discussing the basics of Dynamic Range Compression (DRC) and the key features of a compressor. Now, let’s delve deeper into one of the critical components of our compressor—the Envelope Detector.

Why Focus on the Envelope Detector?

The envelope detector is the heart of the compressor’s control circuit. It tracks the amplitude variations of the input signal, enabling the compressor to respond appropriately to dynamic changes. By accurately detecting the envelope, the compressor can apply gain reduction smoothly and consistently, preserving the musicality and natural feel of the audio.

In this post, we’ll explore an innovative approach to envelope detection by utilizing an analog Hilbert transform filter. This method offers potential advantages in accuracy and responsiveness over traditional rectifier-based detectors.

2. The Envelope Detector

Understanding the Role of the Envelope Detector

An envelope detector is a circuit that extracts the amplitude envelope of a signal. In the context of audio compression, it provides a control voltage that reflects the instantaneous amplitude of the input signal. This control voltage is then used to adjust the gain of the compressor’s variable gain element, thereby achieving the desired compression effect.

Traditional Envelope Detection Methods

Traditionally, envelope detectors in analog compressors use:

While effective, these methods can introduce distortion, have limited frequency response, or may not accurately capture rapid changes in the signal’s envelope.

Introducing the Hilbert Transform Approach

To overcome these limitations, we propose using an analog Hilbert transform to create an envelope detector. The Hilbert transform allows us to construct the analytic signal of the input, from which we can precisely compute the instantaneous amplitude (envelope) without altering the signal’s amplitude or introducing significant distortion.

3. Theory of the Hilbert Transform

3.1. Mathematical Definition

The Hilbert transform is a fundamental tool in signal processing, providing a way to create a signal that is phase-shifted by \(-90^\circ\) at every frequency component of the original signal. It is defined for a real-valued function \(x(t)\) as:

\[ \hat{x}(t) = \mathcal{H}\{ x(t) \} = \frac{1}{\pi} \, \text{P.V.} \int_{-\infty}^{\infty} \frac{x(\tau)}{t - \tau} \, d\tau \]

Where:

In the frequency domain, the Hilbert transform has a simple representation. If \(X(f)\) is the Fourier Transform of \(x(t)\), then the Fourier Transform of \(\hat{x}(t)\) is:

\[ \hat{X}(f) = -j \cdot \text{sgn}(f) \cdot X(f) \]

Where:

This operation effectively introduces a \(-90^\circ\) phase shift to all positive frequencies and a \(+90^\circ\) shift to all negative frequencies.

3.2. The Analytic Signal

The analytic signal \(z(t)\) is a complex signal constructed from \(x(t)\) and its Hilbert transform \(\hat{x}(t)\):

\[ z(t) = x(t) + j \cdot \hat{x}(t) \]

The analytic signal has the following properties:

3.3. Why Use the Hilbert Transform for Envelope Detection?

Advantages Over Traditional Methods

Using the Hilbert transform to obtain the envelope offers several benefits:

Challenges

Implementing the Hilbert transform in analog hardware is challenging due to:

Despite these challenges, careful design of the filter network can yield a practical approximation suitable for our compressor.

4. Hilbert Transform Envelope Detector: A System Overview

Having established the theoretical foundation, let’s now look at how to implement the Hilbert transform envelope detector in practice.

4.1. System Components

The envelope detector based on the Hilbert transform consists of the following key components:

  1. Input Signal: The original audio signal \(x(t)\) that we wish to process.
  2. Hilbert Transform Filter: An analog filter network that approximates the Hilbert transform, providing \(\hat{x}(t)\).
  3. Complex Signal Construction: Combining \(x(t)\) and \(\hat{x}(t)\) to form the analytic signal \(z(t)\).
  4. Envelope Extraction: Computing the magnitude \(|z(t)|\) to obtain the envelope.

4.2. Detailed Breakdown

1. Input Signal (\(x(t)\))

2. Hilbert Transform Filter

3. Complex Signal Combination

4. Envelope Extraction

4.3. Advantages of This Approach

4.4. System Diagram

To visualize the flow of signals in the envelope detector, consider the following block diagram:

  1. Input Signal (\(x(t)\)): The original audio signal.
  2. Hilbert Transform Filter: Processes \(x(t)\) to produce \(\hat{x}(t)\).
  3. Squaring Circuits: Square both \(x(t)\) and \(\hat{x}(t)\) separately.
  4. Summing Circuit: Add the squared signals to obtain \(x(t)^2 + \hat{x}(t)^2\).
  5. Square Root Circuit (Optional): Calculate the square root to get \(|z(t)|\).
  6. Output Envelope: The resulting envelope signal used for gain control.

5. Hilbert Transform Filter Design

Now that we understand the role of the Hilbert transform in envelope detection, let’s move on to designing the filter that will implement it. As mentioned, the key component in the analog implementation of the Hilbert transform is an all-pass filter network. This filter will create the 90-degree phase shift needed for the imaginary part of the analytical signal.

5.1. Understanding All-Pass Filters

An all-pass filter is essential for creating the required phase shift without affecting the signal amplitude. Its primary function is to alter the phase of the input signal in a frequency-dependent manner while maintaining a flat amplitude response across all frequencies.

Transfer Function of a First-Order All-Pass Filter:

The transfer function \(H(s)\) of a first-order all-pass filter in the Laplace domain is:

\[ H(s) = \frac{s - \omega_0}{s + \omega_0} \]

Where:

Phase Response:

The phase shift \(\phi(f)\) introduced by the filter at a frequency \(f\) is:

\[ \phi(f) = -2 \arctan\left( \frac{f}{f_0} \right) \]

This equation shows that the phase shift depends on the frequency \(f\) and the corner frequency \(f_0\). As \(f\) increases, the phase shift approaches (-180^).

However, for our Hilbert transformer, we aim for a (-90^) phase shift across the audio frequency band (20 Hz to 20 kHz). Since a single first-order all-pass filter cannot provide a constant (-90^) phase shift over such a wide range, we need to cascade multiple all-pass filter stages.

5.1.1. Simulating the Single All-Pass Filter

To visualize how a single first-order all-pass filter behaves, let’s simulate its frequency response using Python. This will help us understand the limitations of a single stage and why cascading multiple stages is necessary.

Python Code:

import numpy as np
import matplotlib.pyplot as plt
import control

# Define the Laplace variable s
s = control.TransferFunction.s

# Corner frequency (f0) set to 1 kHz
f0 = 1000  # Hz
omega0 = 2 * np.pi * f0  # rad/s

# Define the transfer function H(s) = (s - ω0) / (s + ω0)
H = (s - omega0) / (s + omega0)

# Frequency range: 10 Hz to 100 kHz
frequencies = np.logspace(1, 5, num=1000)  # Hz
omega = 2 * np.pi * frequencies  # rad/s

# Compute the magnitude and phase response
mag, phase, omega = control.bode(H, omega, plot=False)

# Convert magnitude to dB
mag_db = 20 * np.log10(mag)

# Convert phase to degrees
phase_deg = np.degrees(phase)

# Unwrap and adjust phase to start at 180 degrees
phase_unwrapped = np.unwrap(phase)
phase_deg_unwrapped = np.degrees(phase_unwrapped)
phase_deg_adjusted = phase_deg_unwrapped + 180  # Adjust to positive degrees

# Plotting the Magnitude Response
plt.figure(figsize=(10, 6))
plt.semilogx(frequencies, mag_db)
plt.title('Magnitude Response of Single All-Pass Filter')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude (dB)')
plt.ylim([-1, 1])  # Magnitude should be around 0 dB
plt.grid(True, which='both', ls='--', lw=0.5)
plt.show()

# Plotting the Phase Response
plt.figure(figsize=(10, 6))
plt.semilogx(frequencies, phase_deg_adjusted)
plt.title('Phase Response of Single All-Pass Filter')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Phase (degrees)')
plt.ylim([0, 200])
plt.grid(True, which='both', ls='--', lw=0.5)
plt.show()

Explanation:

Interpretation:

Plots:

Figure 1: Magnitude Response of Single All-Pass Filter

Magnitude Response Single All-Pass Filter

Figure 2: Phase Response of Single All-Pass Filter

Phase Response Single All-Pass Filter

5.2. Cascading All-Pass Filters for the Hilbert Transform

By cascading multiple first-order all-pass filters, we can approximate a constant \(-90^\circ\) phase shift over the desired frequency band. Each stage contributes to the total phase shift, and by carefully selecting the corner frequencies \(f_0\) of each stage, we can distribute the phase shifts to achieve a near-flat response.

Total Phase Shift of Cascaded Filters:

For \(N\) cascaded first-order all-pass filters, the total phase shift is:

\[ \phi_{\text{total}}(f) = -2N \arctan\left( \frac{f}{f_0} \right) \]

If we use different \(f_0\) values for each stage, the total phase shift becomes:

\[ \phi_{\text{total}}(f) = -2 \sum_{k=1}^{N} \arctan\left( \frac{f}{f_{0k}} \right) \]

Where \(f_{0k}\) is the corner frequency of the \(k\)-th stage.

5.3. Designing the Filter Stages

1. Defining the Frequency Band:

Our target is the full audio spectrum, from \(f_{\text{min}} = 20 \text{ Hz}\) to \(f_{\text{max}} = 20 \text{ kHz}\). We need to approximate a (-90^) phase shift across this entire range.

2. Choosing the Number of Stages (\(N\)):

We’ll use 4 stages as a balance between performance and complexity.

3. Determining the Corner Frequencies (\(f_{0k}\)):

We distribute the corner frequencies logarithmically across the frequency band using:

\[ r = \left( \frac{f_{\text{max}}}{f_{\text{min}}} \right)^{\frac{1}{N}} \approx 5.623 \]

For \(k = 1\) to \(N\):

\[ f_{0k} = f_{\text{min}} \cdot r^{k - 0.5} \]

Calculated Corner Frequencies:

5.3.1. Simulating the Cascaded All-Pass Filters

To visualize the combined effect of cascading multiple all-pass filters, let’s simulate the frequency response of the four-stage filter designed above.

Python Code:

import numpy as np
import matplotlib.pyplot as plt

# Frequency range: 10 Hz to 100 kHz
f = np.logspace(1, 5, num=1000)
omega = 2 * np.pi * f

# Corner frequencies for each stage
f0_list = [47.42, 266.6, 1498.8, 8425.6]  # Hz
omega0_list = [2 * np.pi * f0 for f0 in f0_list]

# Initialize the total transfer function H_total
H_total = np.ones_like(omega, dtype=complex)

# Multiply the transfer function of each stage
for omega0 in omega0_list:
    H_stage = (1j * omega - omega0) / (1j * omega + omega0)
    H_total *= H_stage

# Magnitude and Phase Response
magnitude = np.abs(H_total)
phase = np.angle(H_total, deg=True)

# Unwrap phase to avoid discontinuities and adjust to start from 0 degrees
phase_unwrapped = np.unwrap(np.angle(H_total)) * (180 / np.pi)
phase_normalized = phase_unwrapped - phase_unwrapped[0]

# Plotting the Magnitude Response
plt.figure(figsize=(10, 6))
plt.semilogx(f, 20 * np.log10(magnitude))
plt.title('Magnitude Response of Cascaded All-Pass Filters')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude (dB)')
plt.grid(True)
plt.show()

# Plotting the Phase Response
plt.figure(figsize=(10, 6))
plt.semilogx(f, phase_normalized)
plt.title('Phase Response of Cascaded All-Pass Filters')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Phase (degrees)')
plt.grid(True)
plt.show()

Explanation:

Interpretation:

Plots:

Figure 3: Magnitude Response of Cascaded All-Pass Filters

Magnitude Response Cascaded All-Pass Filters

Figure 4: Phase Response of Cascaded All-Pass Filters

Phase Response Cascaded All-Pass Filters

(Note: Replace the placeholders above with the actual plots generated by the code.)

5.4. Calculating Component Values

For each all-pass filter stage, the corner frequency \(f_{0k}\) is related to the resistor (\(R_k\)) and capacitor (\(C_k\)) values by:

\[ f_{0k} = \frac{1}{2\pi R_k C_k} \]

We can choose standard capacitor values and calculate the required resistor values.

Assuming \(C_k = 10 \text{ nF}\):

Selecting Standard Resistor Values:

Adjust \(C_k\) slightly if necessary to match standard resistor values more closely.

5.5. Implementing the All-Pass Filter Stages

Circuit Configuration:

A common configuration for a first-order all-pass filter uses an operational amplifier (Op-Amp) with the following component connections:

Op-Amp Selection:

Choose an Op-Amp suitable for audio frequencies, such as the TL072 or NE5532, which offer low noise and good bandwidth.

5.6. Cascading the Filters

Connect the output of each all-pass filter stage to the input of the next stage. Ensure that the Op-Amps are properly powered with a dual supply (e.g., \(\pm15 \text{ V}\)) and that coupling capacitors are used if necessary to block DC offsets.

5.7. Simulation and Verification

Before building the circuit, it’s wise to simulate it using software like LTspice or Multisim.

Simulation Steps:

  1. Model Each Stage:

    Input the calculated component values for each stage into the simulation.

  2. Run AC Analysis:

    Perform an AC analysis to plot the phase response across the frequency band (20 Hz to 20 kHz).

  3. Verify Phase Shift:

    Check that the total phase shift approximates \(-90^\circ\) across the desired frequency range.

Expected Results:

5.8. Practical Considerations

Component Tolerances:

Use precision resistors (1% or better) and capacitors to minimize deviations due to component tolerances.

Bandwidth Limitations:

Be mindful of the Op-Amp’s bandwidth to ensure it can handle frequencies up to 20 kHz without significant phase lag.

Parasitic Effects:

At high frequencies, parasitic capacitance and inductance can affect the circuit’s performance. Keep leads short and use proper grounding techniques.

5.9. Finalizing the Design

By following these steps, we’ve designed an all-pass filter network that approximates a \(-90^\circ\) phase shift across the audio spectrum. This network serves as the Hilbert transformer in our envelope detector.

Key Takeaways:


Tip: If you prefer to use second-order all-pass filters for a steeper phase transition, the design becomes more complex but can provide better phase accuracy over the frequency band. However, for most audio applications, cascading first-order filters as described should suffice.

By refining the design with these detailed calculations and practical steps, we now have a comprehensive guide to building the Hilbert transform filter for our analog compressor. This not only deepens our understanding but also provides valuable insights for anyone interested in replicating or learning from this design.

In the next section, we’ll move on to building the circuit on a breadboard and testing its performance.

6. Building the Circuit

Now that we’ve designed the Hilbert transform filter, it’s time to build it on a breadboard. This will allow us to implement the filter stages we discussed and verify that the circuit provides the required 90-degree phase shift across the audio spectrum.

6.1. Components Needed

To construct the filter, you’ll need the following components:

6.2. Circuit Assembly

  1. Stage 1: Assemble the First All-Pass Filter Stage

    • Start by assembling the first all-pass filter stage using the Op-Amp, resistors, and capacitors. The configuration should follow the schematic provided earlier, where the phase shift is determined by the values of \(R_1\) and \(C_1\).
    • Choose values for \(R_1\) and \(C_1\) that give you a phase shift targeting the lower end of the audio spectrum (e.g., 100 Hz to 1 kHz). For example, you could use R = 10 kΩ and C = 15.9 nF for a 1 kHz cutoff.

    Tip: Here’s a great all-pass filter guide from Analog Devices to help you understand the filter’s behavior in detail.

  2. Stage 2: Cascade Additional All-Pass Filter Stages

    • After you’ve assembled the first stage, cascade additional all-pass filter stages by connecting the output of one stage to the input of the next. Each stage should be tuned to a different portion of the audio spectrum to achieve an even phase shift.
    • For example:
      • Stage 1: \(f_0 = 100 \text{ Hz}\)
      • Stage 2: \(f_0 = 1 \text{ kHz}\)
      • Stage 3: \(f_0 = 5 \text{ kHz}\)
      • Stage 4: \(f_0 = 15 \text{ kHz}\)

    Adjust the component values of each stage accordingly, using the formula \(f_0 = \frac{1}{2 \pi R C}\). If you need help calculating values, you can use this calculator for quick reference.

    Op-Amp Selection: Make sure your chosen Op-Amp can handle the full frequency range of your signal. For higher precision, the OPA2134 or NE5532 are great options for audio processing.

  3. Testing the Circuit

    • Once all stages are connected, apply an input signal (such as a sine wave from a function generator). Measure the output with an oscilloscope to verify that each frequency component exhibits a 90-degree phase shift.
    • At lower frequencies (e.g., 100 Hz), the phase shift should approach 0 degrees, and at higher frequencies (e.g., 20 kHz), the phase shift should approach 90 degrees.

    Simulation: Before physically building the circuit, it’s often useful to simulate it using software like LTspice or TINA-TI. You can find free tools like LTspice from Analog Devices that let you model the circuit and verify the phase shift across the frequency range.

6.3. Final Thoughts

Building this circuit is a hands-on way to better understand how an active all-pass filter works in practice. By cascading multiple stages, you can spread the 90-degree phase shift evenly across the audio spectrum, and once verified, this filter can be incorporated into the larger Hilbert transform system for envelope detection.

7. Analyzing the Envelope Detector

Once the Hilbert transform filter is built, we will analyze its performance by feeding it an audio signal and observing the output. We will use an oscilloscope to visualize the phase shift and the envelope detection.

8. Conclusion

In this post, we focused on the envelope detector based on an analog Hilbert transform filter. We covered the theory, design, and construction of the filter. In the next post, we will delve deeper into the analysis of the envelope detector and start working on the next component of our analog compressor.

Stay tuned for more updates as we continue this exciting journey of building an analog compressor and turning it into a VST plugin!


Feel free to leave comments or questions below. Happy building!