Chapter 1 Preview

The second edition of Think DSP is not for sale yet, but if you would like to support this project, you can buy me a coffee.

Chapter 1 Preview#

Click here to run this notebook on Colab.

Hide code cell content

import importlib, sys
sys.modules["imp"] = importlib

%load_ext autoreload
%autoreload 2

Hide code cell content

# download thinkdsp.py

from os.path import basename, exists

def download(url):
    filename = basename(url)
    if not exists(filename):
        from urllib.request import urlretrieve
        local, _ = urlretrieve(url, filename)
        print('Downloaded ' + local)
        
download("https://github.com/AllenDowney/ThinkDSP/raw/master/thinkdsp/thinkdsp.py")
Downloaded thinkdsp.py
import matplotlib.pyplot as plt

from thinkdsp import decorate

Read a wave#

read_wave reads WAV files. The WAV examples in the book are from freesound.org. In the contributors section of the book, I list and thank the people who uploaded the sounds I use.

Hide code cell content

# download violin.wav

from os.path import basename, exists

def download(url):
    filename = basename(url)
    if not exists(filename):
        from urllib.request import urlretrieve
        local, _ = urlretrieve(url, filename)
        print('Downloaded ' + local)
        
download("https://github.com/AllenDowney/ThinkDSP/raw/master/soln/92002__jcveliz__violin-origional.wav")
from thinkdsp import read_wave

wave = read_wave('92002__jcveliz__violin-origional.wav')
wave.make_audio()

I pulled out a segment of this recording where the pitch is constant. When we plot the segment, we can’t see the waveform clearly, but we can see the “envelope”, which tracks the change in amplitude during the segment.

start = 1.2
duration = 0.6
segment = wave.segment(start, duration)
segment.plot()
plt.xlabel('Time (s)');
_images/b0d5be30426bf4151a5269ef6b2e88f4381cc0cac96459b6d8588678b17b1adc.png

Spectrums#

Wave provides make_spectrum, which computes the spectrum of the wave.

spectrum = segment.make_spectrum()

Spectrum provides plot

spectrum.plot()
plt.xlabel('Frequency (Hz)');
_images/b36a74c61fbd6eebf9cd99aeaa3d781b4fa6b5b8a00cc25cf22f3bbfbc180fcc.png

The frequency components above 10 kHz are small. We can see the lower frequencies more clearly by providing an upper bound:

spectrum.plot(high=10000)
plt.xlabel('Frequency (Hz)');
_images/e4a3e6e8db3981e612020f5fc52b9780dab287f360ed07e2f84db94ef91c4ab9.png

Spectrum provides low_pass, which applies a low pass filter; that is, it attenuates all frequency components above a cutoff frequency.

spectrum.low_pass(3000)

The result is a spectrum with fewer components.

spectrum.plot(high=10000)
plt.xlabel('Frequency (Hz)');
_images/0cdab69415a9f56a55c34a376c6e477df5bc3108026633ec96e47546b5937dea.png

We can convert the filtered spectrum back to a wave:

filtered = spectrum.make_wave()

Now we can listen to the original segment and the filtered version.

segment.make_audio()
filtered.make_audio()

The original sounds more complex, with some high-frequency components that sound buzzy.

The filtered version sounds more like a pure tone, with a more muffled quality.

The cutoff frequency I chose, 3000 Hz, is similar to the quality of a telephone line, so this example simulates the sound of a violin recording played over a telephone.

Interaction#

The following shows the same example using interactive IPython widgets.

def filter_wave(wave, start, duration, cutoff):
    """Selects a segment from the wave and filters it.
    
    Plots the spectrum and displays an Audio widget.
    
    wave: Wave object
    start: time in s
    duration: time in s
    cutoff: frequency in Hz
    """
    segment = wave.segment(start, duration)
    spectrum = segment.make_spectrum()

    spectrum.plot(color='0.7')
    spectrum.low_pass(cutoff)
    spectrum.plot()
    plt.xlabel('Frequency (Hz)');
    plt.show()
    
    audio = spectrum.make_wave().make_audio()
    display(audio)

Adjust the sliders to control the start and duration of the segment and the cutoff frequency applied to the spectrum.

from ipywidgets import interact, fixed
from IPython.display import display

wave = read_wave('92002__jcveliz__violin-origional.wav')
interact(filter_wave, wave=fixed(wave), 
         start=(0, 5, 0.1), duration=(0, 5, 0.1), cutoff=(0, 10000, 100));

Think DSP: Digital Signal Processing in Python, 2nd Edition

Copyright 2024 Allen B. Downey

License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International