Signal Processing Foundations — Week 3

LTI Systems &
1D Convolution

From the impulse response DNA to the sliding-window sum — master the computational engine behind every noise filter, audio effect, and convolutional neural network layer.

System Operator T{·} Superposition Impulse Response h[n] y[n] = x[n] ∗ h[n] BIBO Stability Echo Synthesis

LTI Systems: Linearity, Time-Invariance & Impulse Response

A system is any rule that transforms an input signal into an output signal. Out of all possible systems, signal processing focuses almost exclusively on LTI systems — those obeying two symmetries (linearity and time-invariance) that make their behavior completely predictable from a single experiment.

After this section you will be able to
  • Test any system for linearity using the superposition property and for time-invariance using the shift test.
  • Compute the impulse response $h[n]$ of a difference equation by substituting $x[n] = \delta[n]$.
  • Classify a system as FIR or IIR and determine BIBO stability using $\sum|h[n]| < \infty$.

Play a middle-C on a digital piano at normal volume: you get a clean note. Play it twice as hard: you get exactly the same note at twice the volume — no distortion, no surprises. Now play two notes simultaneously: the output is the sum of the two notes processed independently, just as you would expect. This predictable, symmetric behavior is what engineers call linearity, and it's the foundation of every digital filter ever built.

🎯
Why this matters: LTI systems are the "perfectly predictable" class. With them, one experiment — feeding the system a single impulse — reveals the system's complete behavior for every input that will ever exist. This collapses infinite-dimensional analysis into one stored array called the impulse response $h[n]$.
🔗
Think of it this way

An LTI system is like a recipe that scales perfectly: double the ingredients, get exactly double the result; make the dish at 6 pm vs. 7 pm and get the exact same dish. Just as a reliable recipe gives consistent, predictable results regardless of quantity or timing, an LTI system gives predictable outputs regardless of input amplitude or when the signal starts.

x[n] System Operator y[n] = T{x[n]} y[n] LTI special case → LTI: h[n] stores all y[n] = x[n] ∗ h[n] y[n] Any mapping rule One impulse test → complete description

Every signal processing algorithm is a system. LTI systems are special: the impulse response $h[n]$ fully captures all behavior.

possible inputs
One impulse test predicts them all for LTI systems
0
output at zero input
Necessary condition for linearity — non-zero output at zero input fails immediately
<∞
Σ|h[n]|
BIBO stability: absolute summability of impulse response
Problem

Linearity — Superposition

A system is linear if scaling and adding inputs produces identically scaled and added outputs. Formally:

📐 Superposition Principle

$$\mathcal{T}\{a \cdot x_1[n] + b \cdot x_2[n]\} = a \cdot \mathcal{T}\{x_1[n]\} + b \cdot \mathcal{T}\{x_2[n]\}$$

Must hold for all signals $x_1, x_2$ and all scalars $a, b \in \mathbb{R}$.

📝 Worked Example — Is $\mathcal{T}\{x[n]\} = 3x[n] + 5$ linear?

Background. Compare $\mathcal{T}\{ax_1+bx_2\}$ against $a\mathcal{T}\{x_1\}+b\mathcal{T}\{x_2\}$.

Problem: Test $\mathcal{T}\{x\} = 3x[n]+5$ with $a=2, b=1$.

1
Left side: $\mathcal{T}\{2x_1+x_2\} = 3(2x_1+x_2)+5 = 6x_1+3x_2+5$
2
Right side: $2\mathcal{T}\{x_1\}+\mathcal{T}\{x_2\} = 2(3x_1+5)+(3x_2+5) = 6x_1+3x_2+15$
3
Compare: LHS $= \ldots+5$, RHS $= \ldots+15$. They differ by 10 — not equal.
4
Zero-input test: $\mathcal{T}\{0\} = 3(0)+5 = 5 \neq 0$. Any linear system must give zero output for zero input.
NON-LINEAR — the constant offset +5 breaks superposition.
✔ Quick Check

Is $\mathcal{T}\{x[n]\} = 5x[n]$ linear? Apply the zero-input test first.

$\mathcal{T}\{0\} = 5 \cdot 0 = 0$ ✓. Then $\mathcal{T}\{ax_1+bx_2\} = 5(ax_1+bx_2) = a\cdot 5x_1 + b\cdot 5x_2$ ✓. LINEAR.
💡

Quick linearity test: Does $\mathcal{T}\{0\}=0$? Any constant offset anywhere in the rule immediately fails linearity.

Time-Invariance & Impulse Response h[n]

A system is time-invariant if delaying the input by $n_0$ delays the output by exactly $n_0$:

⌛ Shift-Invariance Condition

$$\text{If } y[n] = \mathcal{T}\{x[n]\},\quad\text{then } y[n-n_0] = \mathcal{T}\{x[n-n_0]\}$$

The system's behavior is identical regardless of when the signal is applied.

📝 Worked Example — Impulse Response of $y[n] = 0.5x[n] - 0.25x[n-1]$

Background. For any LTI system, substituting $x[n]=\delta[n]$ reads off $h[n]$ directly.

Problem: Find $h[n]$ for $\mathcal{T}\{x[n]\} = 0.5x[n] - 0.25x[n-1]$.

1
Substitute: $h[n] = 0.5\,\delta[n] - 0.25\,\delta[n-1]$
2
$n=0$: $h[0] = 0.5\cdot\delta[0] - 0.25\cdot\delta[-1] = 0.5(1) - 0.25(0) = \mathbf{0.5}$
3
$n=1$: $h[1] = 0.5\cdot\delta[1] - 0.25\cdot\delta[0] = 0 - 0.25 = \mathbf{-0.25}$
4
$n \geq 2$: all $\delta$ terms are zero, so $h[n] = \mathbf{0}$.
h = [0.5, −0.25] — FIR, exactly 2 non-zero taps.
✔ Quick Check

Is this system BIBO stable? Compute $\sum|h[n]|$.

$\sum|h[n]| = |0.5| + |-0.25| = 0.75 < \infty$ → STABLE.
Unit Impulse δ[n] LTI System T{·} Impulse Resp. h[n] = T{δ[n]} Any Output y[n]=x[n]∗h[n]

One impulse test produces $h[n]$; convolution with any $x[n]$ then gives the exact output for that input.

BIBO Stability Condition

A system is Bounded-Input Bounded-Output stable if and only if its impulse response is absolutely summable:

$$\sum_{n=-\infty}^{\infty} |h[n]| < \infty$$

📝 Worked Example — Stability Test for $h[n] = (0.7)^n u[n]$

Problem: Is $h[n] = (0.7)^n u[n]$ BIBO stable?

1
$\sum_{n=0}^{\infty}|h[n]| = \sum_{n=0}^{\infty}(0.7)^n$ — an infinite geometric series with $r=0.7$.
2
Geometric sum: $\dfrac{1}{1-r} = \dfrac{1}{1-0.7} = \dfrac{1}{0.3} \approx 3.33 < \infty$ ✓
3
If $r \geq 1$ the sum diverges → UNSTABLE.
STABLE — |r| = 0.7 < 1 guarantees convergence.
⚠️
Common Mistake

Myth: "Any system that multiplies the input by a constant is linear."

Reality: $y[n] = 3x[n] + 5$ multiplies by 3 but is NOT linear — the constant +5 means $\mathcal{T}\{0\} = 5 \neq 0$. Only systems where every term contains the input (no standalone constants) can be linear.

Solution
🤔 Pause & Predict

Switch the widget below to "Non-linear: y = x²". Before touching the sliders: do you predict that the green expected curve (superposition) will match the red actual output?

Form your prediction first — then select the system type to verify ↓

Try It: Superposition Tester

Switch between system types. Adjust scales $a$ and $b$. Watch whether the green expected curve (superposition) matches the red actual output — they align only for linear systems.

1.5
0.5
✅ Superposition holds — this system is LINEAR.
x₁[n] x₂[n] Expected: a·y₁ + b·y₂ Actual: T{a·x₁ + b·x₂}
Live Calculation — Superposition Check at x₁=sin, x₂=cos
Expected:a·T{x₁[0]} + b·T{x₂[0]}
Actual:T{a·x₁[0] + b·x₂[0]}
Match?|Expected − Actual| < 0.001
Implementation
Python · NumPy & SciPy — Testing LTI Properties & Impulse Response
import numpy as np import scipy.signal as sig # ── Linearity test ──────────────────────────────────────────── x1 = np.array([1.0, -0.5, 2.0, 0.3]) x2 = np.array([0.5, 1.0, -1.0, 0.8]) a, b = 2.0, -1.5 for name, T in [("3x", lambda x: 3*x), ("3x+5", lambda x: 3*x+5)]: lhs = T(a*x1 + b*x2) rhs = a*T(x1) + b*T(x2) print(f"T={name}: Linear? {np.allclose(lhs,rhs)}") # ── Impulse response ───────────────────────────────────────── b_coeffs = np.array([0.5, -0.25, 0.1]) a_coeffs = np.array([1.0]) delta = sig.unit_impulse(10, idx=0) h = sig.lfilter(b_coeffs, a_coeffs, delta) print(f"h[n] = {np.round(h, 4)}") print(f"BIBO stable: {np.sum(np.abs(h)) < 1e6}")
Output
T=3x: Linear? True T=3x+5: Linear? False ← constant +5 breaks superposition h[n] = [ 0.5 -0.25 0.1 0. 0. 0. 0. 0. 0. 0. ] BIBO stable: True
Key Takeaway

An LTI system's entire behavior — for every possible input that will ever exist — is encoded in one array: the impulse response $h[n]$, obtained by feeding the system a single unit impulse.

🎚️
Real-World Application

Digital Audio Equalizers

Every digital equalizer — from the one on your phone to a professional studio console — relies entirely on the LTI assumption. Because audio processing is linear and time-invariant, an engineer can apply bass boost and treble cut as independent filters and simply add their outputs. If the console were non-linear, bass and treble would interact unpredictably and couldn't be processed independently. LTI is the mathematical guarantee that a 31-band parametric EQ works exactly as advertised.

✦ Checkpoint Check Your Understanding — LTI Systems

Q1State the zero-input test for linearity. If $\mathcal{T}\{0\} = 7$, is the system linear?

Answer: A linear system must give zero output for zero input. Since $\mathcal{T}\{0\} = 7 \neq 0$, the system is NOT linear.

Q2Find $h[n]$ for $y[n] = 0.8x[n] - 0.4x[n-1]$. Is the system FIR or IIR?

Answer: Substitute $\delta[n]$: $h[0]=0.8$, $h[1]=-0.4$, $h[n]=0$ for $n \geq 2$. So $h=[0.8, -0.4]$ — FIR (finite, 2-tap).

Q3Why does a time-varying coefficient (e.g. $y[n] = n \cdot x[n]$) make a system time-varying, even if it is linear?

Answer: The multiplier $n$ grows with the sample index, so the system's gain changes over time. Delaying the input gives $n \cdot x[n-1]$, but delaying the output gives $(n-1) \cdot x[n-1]$ — these differ, violating the shift-invariance condition.

1D Convolution: y[n] = x[n] ∗ h[n]

Convolution is the single operation that computes every LTI system's output. It slides the impulse response over the input, computing a weighted sum at each position. Master the flip-and-slide mechanic and you understand the mathematical core of every noise filter, audio effect, and CNN layer.

After this section you will be able to
  • Compute $y[n] = x[n] * h[n]$ by hand for short sequences using the convolution sum, showing every multiplication step.
  • State the output length formula $L = N + M - 1$ and explain why convolution grows the signal.
  • Implement convolution in Python using both a manual nested loop and np.convolve(), and verify their results match.

Imagine running a weighted magnifying glass along a row of numbers. At each position it sits over a small neighborhood, multiplies each value by a weight, sums everything up, and writes down one output number. Slide it one step right, repeat. That systematic sliding-weighted-sum is exactly what convolution does — and it's why every CNN layer, every audio filter, every edge detector is mathematically the same operation.

🎯
Why this matters: Convolution connects the impulse response you measured in Topic 1 to every future input: $y[n] = x[n] * h[n]$. Once you own this mechanic you can design noise filters, synthesize reverb, and understand how every convolutional neural network layer extracts features from raw data.
🔗
Think of it this way

Convolution is like a barista's recipe card sliding along a drink order: at each position they read the "weights" from the card, multiply by the quantities in the order, add the results, and pour one output serving. Slide the card to the next item, repeat. The card is $h[n]$, the order is $x[n]$, and each poured serving is one sample of $y[n]$.

$x[n]$
Input Signal
The signal you want to process — length $N$
e.g. [1, 2, 0, −1], N=4
$h[n]$
Impulse Response
The system's "filter weights" — length $M$
e.g. [0.5, 1.0, 0.5], M=3
$y[n]$
Output Signal
Filtered result — length $L = N+M-1$
e.g. 4+3−1 = 6 samples
$k$
Sum Index
Ranges over all valid samples of $x[k]$
0 ≤ k ≤ N−1
N+M−1
output length
Full convolution always grows the signal
N×M
multiplications
Direct convolution cost — $\mathcal{O}(NM)$
N log N
FFT-based
Fast convolution via FFT — used in audio DAWs
Problem

The Convolution Sum

📐 Discrete Convolution Definition

$$y[n] = (x * h)[n] = \sum_{k=-\infty}^{\infty} x[k]\;h[n-k]$$

$y[n] = \displaystyle\sum_{k} x[k]\; h[n-k]$
$y[n]$
Output
One sample of the filtered signal at time $n$
$x[k]$
Input
Input samples summed over all valid $k$
$h[n-k]$
Flipped & Shifted
Impulse response: flipped and shifted to position $n$
$k$
Slide Index
Steps through the overlap region
  • For each output sample $y[n]$: flip $h$, shift by $n$, multiply with $x$, sum everything.
  • Output length: for $x$ of length $N$ and $h$ of length $M$, output length $L = N + M - 1$.
📝 Worked Example — Full Manual Convolution

Background. $y[n] = \sum_k x[k]\,h[n-k]$. For finite signals, sum only over the active support of $x$.

Problem: Compute $y = x * h$ where $x = [1, 2, 0, -1]$ (N=4) and $h = [0.5, 1.0, 0.5]$ (M=3).

1
Output length: $L = 4 + 3 - 1 = \mathbf{6}$. Compute $y[0]$ through $y[5]$.
2
$y[0] = x[0]h[0] = (1)(0.5) = \mathbf{0.5}$
3
$y[1] = x[0]h[1] + x[1]h[0] = (1)(1.0) + (2)(0.5) = 1.0 + 1.0 = \mathbf{2.0}$
4
$y[2] = x[0]h[2] + x[1]h[1] + x[2]h[0] = (1)(0.5) + (2)(1.0) + (0)(0.5) = \mathbf{2.5}$
5
$y[3] = x[1]h[2] + x[2]h[1] + x[3]h[0] = (2)(0.5)+(0)(1.0)+(-1)(0.5) = \mathbf{0.5}$
6
$y[4] = (0)(0.5)+(-1)(1.0) = \mathbf{-1.0}$; $\;y[5] = (-1)(0.5) = \mathbf{-0.5}$
y = [0.5, 2.0, 2.5, 0.5, −1.0, −0.5] — 6 output samples from 4-sample input + 3-tap filter.
✔ Quick Check

What is the output length if $x$ has 7 samples and $h$ has 4 taps?

$L = N + M - 1 = 7 + 4 - 1 = 10$ samples.
x[n]: 1 2 0 -1 h[n-k]: 0.5 1.0 0.5 Σ x[k]h[n-k] = y[n] ① Input signal (fixed) ② Filter kernel (flipped, sliding) ③ Weighted sum → one output sample slide →

At each position $n$: flip $h$, overlay on $x$, multiply element-wise, sum → one sample of $y[n]$. Slide one step and repeat.

Key Properties of Convolution

PropertyFormulaPhysical Meaning
Commutativity$x*h = h*x$Order of operands doesn't matter
Associativity$(x*h_1)*h_2 = x*(h_1*h_2)$Cascaded filters can be merged
Distributivity$x*(h_1+h_2)=(x*h_1)+(x*h_2)$Parallel filters can be combined
Identity$x*\delta = x$Convolving with impulse = no change

FIR vs. IIR System Families

PropertyFIRIIR
Duration of h[n]Finite (N taps)Infinite (never zero)
Feedback loops?NoYes (recursive)
Always stable?YesDepends on design
ExampleMoving averageExponential smoother
📝 Worked Example — Commutativity Verification

Problem: Show $x * h = h * x$ for $x=[1,2]$, $h=[3,4]$.

1
$x*h$: $y[0]=(1)(3)=3$; $y[1]=(1)(4)+(2)(3)=4+6=10$; $y[2]=(2)(4)=8$. Result: $[3,10,8]$.
2
$h*x$: $y[0]=(3)(1)=3$; $y[1]=(3)(2)+(4)(1)=6+4=10$; $y[2]=(4)(2)=8$. Result: $[3,10,8]$.
Both give [3, 10, 8] — commutativity confirmed.
💡
Key Insight

Convolution always increases signal length: $L = N + M - 1$. A 1-second audio recording convolved with a 2-second reverb impulse response produces a 3-second output — the reverb "tail" is physically real energy that extends past the input. This is not a side effect; it is correct and expected.

Solution
🤔 Pause & Predict

In the interactive below, set the kernel to "Moving Avg (3-tap)" and drag the position slider to $n=2$. Before checking: what value do you predict for $y[2]$ using the convolution sum with $x = [0.5, 1.0, 1.8, \ldots]$ and $h = [1/3, 1/3, 1/3]$?

Compute $y[2] = \frac{1}{3}(x[0]+x[1]+x[2])$ first, then drag the slider to verify ↓

Try It: Interactive Sliding Convolution

Drag the Position slider to slide the kernel $h[k]$ across the input $x[n]$. The highlighted region shows the overlap; the right panel accumulates the output samples computed so far.

n = 0
Input x[n] & Kernel position
Output y[n] computed so far
x[n] input Kernel overlap (active region) y[n] output so far
Live Calculation — Convolution Sum at Current Position
y[n] =Σ x[k]·h[n−k] for active k values
Terms:drag slider to compute
Implementation
Python · NumPy — Manual & Library Convolution
import numpy as np x = np.array([1, 2, 0, -1], dtype=float) h = np.array([0.5, 1.0, 0.5], dtype=float) # Method 1: Manual nested loop (shows the math) L = len(x) + len(h) - 1 y_manual = np.zeros(L) for n in range(L): for k in range(len(x)): if 0 <= n - k < len(h): y_manual[n] += x[k] * h[n - k] # Method 2: NumPy built-in (O(NM) direct) y_np = np.convolve(x, h) print("Manual: ", y_manual) print("NumPy: ", y_np) print("Match: ", np.allclose(y_manual, y_np)) print("Commutative: ", np.allclose(np.convolve(x,h), np.convolve(h,x)))
Output
Manual: [ 0.5 2. 2.5 0.5 -1. -0.5] NumPy: [ 0.5 2. 2.5 0.5 -1. -0.5] Match: True Commutative: True
Key Takeaway

Convolution $y[n] = \sum_k x[k]\,h[n-k]$ is the flip-and-slide weighted sum that computes every LTI system's output — and it is mathematically identical to what every CNN layer does to feature maps.

🧠
Real-World Application

Convolutional Neural Networks (CNNs)

Every convolutional layer in a CNN performs exactly the same sliding-window operation described here — applied to 2D image arrays instead of 1D signals. The learned kernel weights $h$ (called "filters" in ML) are trained by gradient descent to detect edges, textures, or other features. A single forward pass through ResNet-50 performs billions of convolution operations. Understanding the 1D convolution sum is the direct prerequisite for understanding how CNNs extract features from raw pixel data.

✦ Checkpoint Check Your Understanding — 1D Convolution

Q1Give the formula for the output length when convolving a signal of length $N$ with a filter of length $M$. What does each extra sample represent physically?

Answer: $L = N + M - 1$. The $M-1$ extra samples are the "ring-down" tail — the filter is still responding to the last input samples after the input has ended (e.g., reverb decay after a sound stops).

Q2Compute $y[0]$ and $y[1]$ for $x=[3, -1, 2]$ and $h=[0.5, 0.5]$ using the convolution sum. Show all multiplication terms.

Answer: $y[0] = x[0]\cdot h[0] = 3 \times 0.5 = 1.5$. $y[1] = x[0]\cdot h[1] + x[1]\cdot h[0] = 3(0.5) + (-1)(0.5) = 1.5 - 0.5 = 1.0$.

Q3Why is $h[n-k]$ written with $n-k$ instead of just $k$? What does the "$n-k$" argument physically represent?

Answer: $h[n-k]$ means the impulse response is flipped (reversed) and shifted to center at position $n$. As $n$ increases by 1, the filter slides one step to the right along the input — that's the "sliding" part of the sliding-window operation.

Echo & Reverb Synthesis, Noise-Cancelling Filters

Convolution is not just theory — it powers every reverb plugin, every noise-cancelling headphone, and every audio signal chain in professional production. This section connects the convolution formula to real acoustic engineering: designing echo impulse responses, cascading filters, and understanding BIBO stability in the context of real systems.

After this section you will be able to
  • Design an echo system's impulse response $h[n] = \delta[n] + \alpha\,\delta[n-d]$ and compute the echo output via np.convolve.
  • Compute the cascade impulse response $h_{total} = h_1 * h_2$ for two series-connected LTI systems.
  • Explain why BIBO stability requires $|\alpha| < 1$ for a feedback echo, and predict the effect of varying the gain.

Stand inside an empty concert hall and clap your hands once, sharply. The burst of sound echoes off the walls, bounces across the ceiling, and slowly fades. Record that entire decay — from the first clap to the last echo — and you have captured the hall's complete acoustic signature. Convolve any music recording with that one measurement, and you recreate the experience of performing inside that exact hall from anywhere in the world.

🎯
Why this matters: Reverb plugins, noise-cancelling headphones, and hearing aids all use this exact principle. The impulse response $h[n]$ is the system's fingerprint — measured once, used to transform any signal. This is the bridge from abstract LTI theory to everyday engineering products.
🔗
Think of it this way

Designing an echo is like setting up a perfect mirror relay: direct sound reaches your ear first ($\delta[n]$), then a quieter reflected copy arrives $d$ samples later ($\alpha\,\delta[n-d]$). Cascade two such relays and the reflections multiply. This is exactly what $h_{total} = h_1 * h_2$ computes — the combined fingerprint of both mirrors in sequence.

1
Dry Input x[n]
Dry input signal
2
h[n] = δ[n]+α·δ[n-d]
Echo impulse response
3
x ∗ h Convolve
Convolution
4
Wet output y[n]
Wet output with echo
13,230
samples
0.3-second echo delay at 44,100 Hz sample rate
<1
|α| required
BIBO stability for feedback echo: gain must be less than 1
88,200
samples in 2-sec RIR
Typical concert hall room impulse response at 44.1 kHz
Problem

Echo Synthesis via Convolution

A simple echo adds a delayed, attenuated copy of the dry signal:

🎵 Echo System Formula

$$y[n] = x[n] + \alpha \cdot x[n - d]$$

$\alpha \in (0,1)$ = echo gain (volume), $d$ = echo delay in samples. At $f_s = 44{,}100$ Hz, a 0.3-second echo: $d = 0.3 \times 44{,}100 = 13{,}230$ samples.

$y[n] = x[n] + \alpha \cdot x[n-d]$
$x[n]$
Direct
The dry signal arriving first
$\alpha$
Echo Gain
Echo volume: must be <1 for stability
$d$
Delay
Echo arrival in samples
📝 Worked Example — Echo Impulse Response with α=0.5, d=4

Background. The echo system is itself LTI. Substitute $x[n]=\delta[n]$ to find $h[n]$.

Problem: Find $h[n]$ for $y[n]=x[n]+0.5\cdot x[n-4]$.

1
Substitute: $h[n] = \delta[n] + 0.5\,\delta[n-4]$
2
$h[0]=\delta[0]=1$; $h[4]=0.5\cdot\delta[0]=0.5$; all other $h[n]=0$.
3
This is FIR: $h=[1, 0, 0, 0, 0.5]$ — non-zero only at indices 0 and 4.
h = [1, 0, 0, 0, 0.5] — sparse FIR; any dry signal convolved with this produces the echo effect.
✔ Quick Check

Is this echo system BIBO stable? Compute $\sum|h[n]|$.

$\sum|h[n]| = 1 + 0.5 = 1.5 < \infty$ → STABLE.

Cascade Systems & BIBO Stability

When two LTI systems are connected in series, the combined impulse response is the convolution of the two individual responses:

🔗 Cascade Equivalence

$$h_{total}[n] = h_1[n] * h_2[n]$$

Two filters in series are equivalent to one filter whose impulse response is their convolution. Order does not matter (commutativity): $h_1 * h_2 = h_2 * h_1$.

📝 Worked Example — Cascade: Edge Detector + Smoother

Problem: $h_1=[1,-1]$ (edge detector), $h_2=[0.5,0.5]$ (smoother). Find $h_{total}$.

1
Output length: $L = 2+2-1 = 3$.
2
$h_{total}[0] = (1)(0.5) = \mathbf{0.5}$
3
$h_{total}[1] = (1)(0.5)+(-1)(0.5) = 0.5-0.5 = \mathbf{0}$
4
$h_{total}[2] = (-1)(0.5) = \mathbf{-0.5}$. Verify reversed order gives same result.
h_total = [0.5, 0, −0.5] — a central-difference bandpass. Order doesn't matter!
x[n] System 1: h₁[n] System 2: h₂[n] y[n] ≡ single: h_total = h₁*h₂ equivalent → h_total[n] = h₁[n] ∗ h₂[n]

Cascaded LTI systems merge into one equivalent system — commutativity means order is always rearrangeable.

Noise-Cancelling via Adaptive FIR

Active noise-cancelling headphones measure the ambient noise $x_{noise}[n]$ with a reference microphone, then convolve it with an adaptive $h[n]$ chosen to produce the exact anti-noise signal:

$$y_{anti}[n] = x_{noise}[n] * h_{ANC}[n] \approx -x_{noise}[n]$$

When $y_{anti}$ reaches your ear simultaneously with the noise, they cancel: $x_{noise} + y_{anti} \approx 0$. The convolution operation — and the LTI framework — is what makes this physically possible.

⚠️
Common Mistake

Myth: "A feedback echo with $|\alpha| = 1$ produces a stable, infinite echo that sustains forever."

Reality: $|\alpha| = 1$ makes the system BIBO unstable. Each echo copy has the same amplitude as the previous, so the output grows without bound — the system accumulates energy indefinitely and will clip or oscillate destructively. Stability requires $|\alpha| < 1$.

Solution
🤔 Pause & Predict

In the widget below, set gain α to 0.9 and delay to 8 samples. Before moving the slider: do you predict the echo copies will grow, stay constant, or decay? What happens if you set α = 0.99?

Check your prediction using the BIBO stability condition $|\alpha| < 1$ first, then verify with the widget ↓

Try It: Echo & Reverb Simulator

Adjust the echo Delay (samples) and Gain α. The canvas shows the impulse response $h[n] = \delta[n] + \alpha\delta[n-d] + \alpha^2\delta[n-2d] + \ldots$ — demonstrating how the echo copies decay geometrically when $|\alpha| < 1$.

8
0.60
Direct: h[0] = 1 Echo copies (×α each time) Full h[n] impulse response
Live Calculation — Echo at current settings
h[0]:= 1 (direct signal)1.0000
h[d] =α = ?
h[2d] =α² = ?
Σ|h[n]| =1/(1-α) = geometric series
Implementation
Python · NumPy — Echo Synthesis Pipeline
import numpy as np fs = 44100 # sample rate (Hz) d = int(0.3 * fs) # 0.3 s echo delay = 13,230 samples alpha = 0.5 # echo attenuation (must be < 1 for BIBO stability) # Build echo impulse response: h[n] = δ[n] + α·δ[n-d] h_echo = np.zeros(d + 1) h_echo[0] = 1.0 # direct signal h_echo[d] = alpha # echo copy at delay d # BIBO stability check print_stability = np.sum(np.abs(h_echo)) print(f"Σ|h[n]| = {print_stability:.4f} → Stable: {print_stability < 1e9}") # Apply echo to a single click (impulse) at sample 1000 x_dry = np.zeros(fs) x_dry[1000] = 1.0 y_wet = np.convolve(x_dry, h_echo) print(f"Click at n=1000 → echo at n={1000+d}") # Cascade: edge detector then smoother h1 = np.array([1, -1], dtype=float) h2 = np.array([0.5, 0.5], dtype=float) h_total = np.convolve(h1, h2) print(f"Cascade h_total = {h_total}")
Output
Σ|h[n]| = 1.5000 → Stable: True Click at n=1000 → echo at n=14230 Cascade h_total = [ 0.5 0. -0.5]
Key Takeaway

Any acoustic effect — echo, reverb, noise cancellation — is just an impulse response $h[n]$ away: measure the system once with an impulse, then apply $y = x * h$ to any dry input to produce the transformed output.

🎹
Real-World Application

Convolution Reverb in Professional DAWs

Convolution reverb plugins in professional Digital Audio Workstations (Logic Pro, Ableton, Pro Tools) implement exactly $y = x * h$ where $h$ is a real-world Room Impulse Response. A single 2-second cathedral RIR at 44.1 kHz contains 88,200 samples. Convolving a 3-minute vocal track with it requires over 700 billion multiplications naively — which is why DAWs use FFT-based fast convolution ($\mathcal{O}(N\log N)$ instead of $\mathcal{O}(NM)$), cutting the computation by three orders of magnitude to run in real time on a laptop CPU.

✦ Checkpoint Check Your Understanding — Echo & Applications

Q1Write the impulse response $h[n]$ for an echo with gain $\alpha = 0.7$ and delay $d = 5$ samples. Is it FIR or IIR?

Answer: $h[n] = \delta[n] + 0.7\,\delta[n-5]$, i.e. $h = [1, 0, 0, 0, 0, 0.7]$. It is FIR — finite duration, non-recursive.

Q2Two filters are cascaded: $h_1=[2, -1]$ and $h_2=[1, 1]$. Compute $h_{total}[0]$, $h_{total}[1]$, and $h_{total}[2]$.

Answer: $h_{total}[0]=(2)(1)=2$; $h_{total}[1]=(2)(1)+(-1)(1)=2-1=1$; $h_{total}[2]=(-1)(1)=-1$. So $h_{total}=[2,1,-1]$.

Q3A noise-cancelling headphone's ANC filter must satisfy $h_{ANC} * x_{noise} \approx -x_{noise}$. What impulse response $h_{ANC}$ achieves exact cancellation, and why is it physically impossible to realize perfectly?

Answer: Exact cancellation needs $h_{ANC} = -\delta[n]$ (negate every sample instantly). Physically impossible because it requires zero processing delay — in practice, a non-causal filter. Real ANC systems introduce a small delay and use adaptive algorithms to approximate this, achieving 20–30 dB reduction rather than perfect cancellation.

Convolution Explorer

Select a filter type and input signal, then watch the full convolution computed in real time. Try the Impulse input to see the impulse response h[n] directly — this is the fundamental test from Topic 1.

Filter h[n]:
Input x[n]:
4.0
Input x[n]
Impulse Response h[n]
Output y[n] = x[n] ∗ h[n]
Filter: 3-pt Moving Avg | h = [1/3, 1/3, 1/3] | Effect: Low-pass smoothing
Input x[n] Impulse response h[n] Output y[n] = x ∗ h

Key Takeaways

The four ideas from this week that every filter designer, audio engineer, and ML practitioner carries permanently.

🔧

System Operator

$y[n]=\mathcal{T}\{x[n]\}$ — any rule mapping input samples to output. Linear: $\mathcal{T}\{0\}=0$ and superposition holds. Time-invariant: shifting the input shifts the output by the same amount.

🧬

Impulse Response h[n]

Feed any LTI system a unit impulse $\delta[n]$ and record the output. That one recording $h[n]$ completely characterizes the system's behavior for every future input via convolution.

🔁

Convolution Sum

$y[n]=\sum_k x[k]\,h[n-k]$. Output length $= N+M-1$. Commutative, associative, distributive. Foundation of every CNN layer, audio filter, and reverb plugin.

🎹

Echo, Reverb & Stability

$h_{echo}[n]=\delta[n]+\alpha\delta[n-d]$ with $|\alpha|<1$ for BIBO stability. Cascade $h_{total}=h_1*h_2$. Real acoustic spaces measured as RIRs and replayed via convolution.

Coming up — Week 4: Discrete Fourier Transform (DFT): Convolution is powerful, but applying a 1,000-point filter to an hour of audio is computationally brutal. Next week, we change perspectives entirely. By viewing signals in the frequency domain via the Discrete Fourier Transform (DFT), we unlock faster algorithms, uncover hidden patterns, and learn how equalizers actually work.

Further Reading & Resources

Curated references to reinforce every concept from this week. Start with the 3Blue1Brown video for the best visual intuition of convolution.

Exercises

Rigorous problem sets covering mathematical derivations and functional coding tasks, followed by advanced synthesis applications.

1 Theory · LTI Systems Easy

Linearity & Time-Invariance Proofs

Test the system $\mathcal{T}\{x[n]\} = 3x[n] + 5$ analytically:
(a) Linearity test: Apply the superposition property with $a=2, b=1$ and two signals $x_1, x_2$. Show whether $\mathcal{T}\{2x_1+x_2\} = 2\mathcal{T}\{x_1\}+\mathcal{T}\{x_2\}$ holds.
(b) Time-invariance test: Show whether $\mathcal{T}\{x[n-n_0]\} = y[n-n_0]$ holds for any delay $n_0$.
(c) State clearly whether each property holds and identify the exact term that breaks it.

Linearity: compare $\mathcal{T}\{ax_1+bx_2\}$ with $a\mathcal{T}\{x_1\}+b\mathcal{T}\{x_2\}$. Time-invariance: substitute $x[n-n_0]$ into the rule — does the constant +5 shift with the input?
2 Code · LTI Systems Easy

Numerical LTI Verification & Impulse Response

(a) Implement two system operators in Python: T1(x) = 3*x and T2(x) = 3*x + 5. Use np.random.randn(4) for $x_1, x_2$ with $a=2, b=-1$. Verify linearity with np.allclose().
(b) For the FIR system $y[n] = 0.5x[n] - 0.25x[n-1] + 0.1x[n-2]$, use scipy.signal.unit_impulse(10) and lfilter([0.5,-0.25,0.1],[1],delta) to compute $h[n]$.
(c) Verify BIBO stability: print $\sum|h[n]|$ and confirm the result is finite.

Use from scipy import signal; delta = signal.unit_impulse(10); h = signal.lfilter(b, a, delta). For stability: np.sum(np.abs(h)) should be finite and small.
3 Theory · 1D Convolution Medium

Manual Sliding-Window Convolution

Given $x[n] = [1, 2, 0, -1]$ (length 4) and $h[n] = [0.5, 1.0, 0.5]$ (length 3):
(a) State the output length $L = N + M - 1$.
(b) Manually compute $y[0]$ through $y[5]$ using the convolution sum $y[n]=\sum_k x[k]\,h[n-k]$. Show every multiplication term for each output sample.
(c) Write the full output sequence and verify that $y[0]+y[1]+\ldots+y[5]$ equals $(\sum x[n]) \times (\sum h[n])$.

For each $n$: $h[n-k]$ is non-zero only when $0 \leq n-k \leq 2$, i.e. $n-2 \leq k \leq n$. List only valid $k$ values at each step to avoid off-by-one errors.
4 Code · 1D Convolution Medium

Implement & Verify Convolution in Python

For $x=[1, 2, 0, -1]$ and $h=[0.5, 1.0, 0.5]$:
(a) Implement a manual convolution using a nested for loop (no library calls).
(b) Verify using np.convolve(x, h). Assert they match with np.allclose.
(c) Demonstrate commutativity: show np.convolve(x,h) equals np.convolve(h,x).
(d) Apply a 3-tap moving average filter $h=[1/3,1/3,1/3]$ to the noisy signal x = np.sin(np.linspace(0,2*np.pi,50)) + 0.5*np.random.randn(50) and print the first 5 values of the smoothed output.

Manual loop: for n in range(L): for k in range(len(x)): if 0 <= n-k < len(h): y[n] += x[k]*h[n-k]. For the noisy case use np.convolve(x, h, mode='valid') to avoid edge effects.
5 Theory · Echo & Reverb Applications Medium

Echo Impulse Response & Cascade Analysis

(a) Write the impulse response $h[n]$ for an echo with $\alpha=0.6$ and $d=5$ samples. List all non-zero values explicitly.
(b) Two filters are cascaded: $h_1=[1,-1]$ (first-difference) and $h_2=[0.5,0.5]$ (averaging). Compute $h_{total}[n] = h_1[n]*h_2[n]$ manually step by step. Then compute $h_2*h_1$ and confirm they are equal (commutativity).
(c) Is the echo system in (a) BIBO stable? Compute $\sum|h[n]|$.

For (a): $h=[1,0,0,0,0,0.6]$. For (b): output length $2+2-1=3$. For (c): $\sum|h[n]|=1+0.6=1.6 <\infty$.
6 Code · Echo & Reverb Applications Medium

Echo Synthesis Pipeline in Python

Synthesize a multi-echo effect:
(a) Create a test signal: a single pulse of value 1 at $n=50$, zero elsewhere, total length 200 samples.
(b) Build the echo impulse response: $h[n]=\delta[n]+0.6\,\delta[n-30]+0.36\,\delta[n-60]$ (three echoes, each 60% of the previous).
(c) Compute the output via np.convolve(x, h). Print the output length.
(d) Find and print the indices of the three largest non-zero values in $y$ — they should be at 50, 80, and 110.

Build h = np.zeros(61); h[0]=1.0; h[30]=0.6; h[60]=0.36. The output should have peaks at 50, 80, and 110. Use np.argsort(np.abs(y))[-3:] to find the three largest indices.
7 Synthesis · Theory: Reverb Complexity Analysis Hard

Direct vs. FFT-Based Convolution Cost

A cathedral reverb uses a Room Impulse Response of $M = 88{,}200$ samples (2 s at 44.1 kHz). The dry vocal recording has $N = 2{,}646{,}000$ samples (1 minute).
(a) Calculate the exact number of multiplications for direct time-domain convolution ($\mathcal{O}(NM)$).
(b) FFT-based convolution uses an FFT of size $L$ = next power of 2 above $N+M-1$. Find $L$ (use $2^{\lceil\log_2(N+M-1)\rceil}$), then estimate total operations as $3L\log_2 L$.
(c) Calculate the speedup ratio. At what input length does FFT convolution become faster than direct convolution (crossover point)?

Direct: $N \times M \approx 2.33 \times 10^{11}$. $N+M-1 \approx 2.73\times10^6$; next power of 2: $L=2^{22}=4{,}194{,}304$. FFT ops $\approx 3\times 4.19\times10^6 \times 22 \approx 2.76\times10^8$. Speedup $\approx 844\times$.
8 Synthesis · Code: IIR Smoothing Pipeline Hard

Exponential Smoothing: IIR System End-to-End

The exponential smoothing filter is $y[n] = \alpha\,x[n] + (1-\alpha)\,y[n-1]$, with $\alpha=0.3$.
(a) Show analytically that $h[n] = \alpha(1-\alpha)^n\,u[n]$ is the impulse response (infinite → IIR).
(b) Implement the filter recursively in Python and apply it to a noisy sine wave: x = np.sin(np.linspace(0,4*np.pi,200)) + 0.5*np.random.randn(200).
(c) Verify BIBO stability: compute $\sum_{n=0}^{500}|h[n]|$ numerically and show it converges to $\alpha/(1-(1-\alpha)) = 1$.
(d) Print the maximum absolute error between the noisy input and the smoothed output to confirm the filter reduces noise.

Recursive: y = np.zeros(len(x)); y[0] = alpha*x[0]; [y.__setitem__(i, alpha*x[i]+(1-alpha)*y[i-1]) for i in range(1,len(x))]. For stability: h = alpha*(1-alpha)**np.arange(501); np.sum(np.abs(h)) should be close to 1.0.