To work on this exercise, you should first work through the Lecture on Fourier Analysis of Signals. The FMP Notebooks mentioned in the lecture overview will be particularly helpful!
Work through the PCP notebook on Complex Numbers and complete exercises 1 and 2.
Work through the PCP notebook on the Exponential Function and complete exercise 1.
Work through the PCP notebook on Signals and Sampling and complete exercises 1 and 2. Be sure that you understand the concepts of sampling, aliasing and beating.
Take an audio file (e.g. your favorite song) and listen to downsampled versions of it at different sampling rates. Can you find signals for which the effect is more pronounced? And can you find signals for which the original and the downsampled version sound (almost) identical? Hint: You can use ipd.Audio
from the IPython library to play audio files in this notebook.
import numpy as np
import soundfile as sf
import IPython.display as ipd
# <your solution goes here>
Work through the FMP notebook on Quantization.
Take the audio signal from Task 3 and experiment with different quantization levels (you can copy the quantize_uniform
function from the FMP notebook to use it here). At which quantization strength do you begin to hear quantization noise? What is the difference to non-uniform quantization from musical/perceptual perspective?
# <your solution goes here>
Work through the PCP notebook on the DFT and FFT (you may skip the part on the FFT for now) and complete exercises 1, 2 and 3. You can also consult the FMP notebook on DFT and FFT for more explanations of the concepts.
YouTube video: "The Most Important Algorithm Of All Time"
We want to compare the naive DFT and the FFT (the Cooley-Tukey algorithm in particular). For this, we first have to understand how the FFT works. Read the FMP notebook on DFT and FFT and Section 2.4.3 of the text book. If you prefer video content, this lecture may also be helpful. You should be able to answer the following questions:
Finally, we want to see how much faster the FFT is in practice. First, implement both a DFT and an FFT for an arbitrary real input sequence below. Then we use the timeit module to compare your implementations. How does the speedup factor change for different sequence lengths? Can you relate this to the computational complexity?
If you want, it will not be difficult to find existing Python implementations of the DFT/FFT (even in the linked material) and you can use them for inspiration. It is however important that you understand the code in detail.
import timeit
def dft(x):
# <your solution goes here>
def fft(x):
# <your solution goes here>
N = 128
x = np.random.rand(N)
num_iterations = 1000
time_dft = timeit.timeit(stmt='dft(x)', globals=globals(), number=num_iterations)
time_fft = timeit.timeit(stmt='fft(x)', globals=globals(), number=num_iterations)
time_fft_np = timeit.timeit(stmt='np.fft.rfft(x)', globals=globals(), number=num_iterations)
print("Average execution time for...\nDFT: %.5fs\nFFT: %.5fs\nFFT (numpy): %.5fs" % (time_dft / num_iterations, time_fft / num_iterations, time_fft_np / num_iterations))
print("FFT is %.2f times faster than DFT." % (time_dft / time_fft))