From 56ffde99f95ce1e7294a3acb38d3b8f7f88a43df Mon Sep 17 00:00:00 2001 From: David Burian Date: Sat, 5 Oct 2024 15:44:25 +0200 Subject: [PATCH] fast fourier transform and related --- beginners_guite_to_asr.md | 89 +++++++++++++++++++++ discrete_fourier_transform.md | 53 +++++++++++++ fft.md | 129 +++++++++++++++++++++++++++++++ imgs/5_primitive_root_of_one.png | Bin 0 -> 38322 bytes primitive_root_of_one.md | 25 ++++++ spectral_analysis_of_sound.md | 9 +++ 6 files changed, 305 insertions(+) create mode 100644 discrete_fourier_transform.md create mode 100644 fft.md create mode 100644 imgs/5_primitive_root_of_one.png create mode 100644 primitive_root_of_one.md create mode 100644 spectral_analysis_of_sound.md diff --git a/beginners_guite_to_asr.md b/beginners_guite_to_asr.md index 5b5118f..5ed7396 100644 --- a/beginners_guite_to_asr.md +++ b/beginners_guite_to_asr.md @@ -24,6 +24,95 @@ $$ } $$ +## Glossary + +### Frame + +Part of the signal in time in between two timestamps. + +### Window, Hop length + +Some functions are **windowed**, meaning they are applied repeatedly for different +frames. *Windows are part of frames*, where window size doesn't have to be equal, +but often is to size of frame. The distance between consecutive windows is +called **hop length**, i.e. the overlap of two consecutive windows. + + +### Frequency, Phase, Amplitude + +If we take *pure sound* we can study its 3 basic properties: + +$$ +a * \sin(2\pi * (ft - \phi), +$$ + +where + +- $a$ is **amplitude** -- height of signal's peak (loudness of it) +- $f$ is **frequency** of the sound -- the number of cycles per second +- $t$ is time in seconds +- $\phi \in [0, 1)$ is **phase** of the signal -- horizontal shift of the sine + wave + + +### Amplitude envelope + +The max of waveform for each frame. Gives an idea of loudness of a signal. +Sensitive to outliers. + +$$ +\text{AE}_t = \max_{i \in t} s(i) +$$ + +where + +- $t$ - frame index +- $i$ - timestamp index in a frame +- $s(i)$ - amplitude at timestamp $i$ + +### Root-mean-square energy + +The root of mean of square amplitude within each frame. Describes mean loudness +within a frame, with bias towards high amplitude (loudnesses) + +$$ +\text{RMS}_t = \sqrt{\sum_{i \in t} s(i)^2} +$$ + +where + +- $t$ - frame index +- $i$ - timestamp index in a frame +- $s(i)$ - amplitude at timestamp $i$ + +### Zero-crossing rate + +Number of times signal crossed `y=0` line (going from positive to negative or +vice-versa). ZCR can tell us about the sound's: + +- percussiveness vs pitch -- both have high ZCR, but percussive sound's ZCR + varies more +- presence/absence of voice -- voiced sounds tend to have higher ZCR +- pitch -- assuming monotonic pitch, ZCR would correspond to frequency + +$$ +\text{ZCR}_t = \frac{1}{2} + \sum_{i \in t} | + \operatorname{sgn}\left(s(i)\right) - + \operatorname{sgn}\left(s(i+1)\right) + | +$$ + +where + +- $t$ - frame index +- $i$ - timestamp index in a frame +- $s(i)$ - amplitude at timestamp $i$ + ## Common approaches +## Good sources of info + +- [Audio Signal Processing for ML + (YouTube)](https://www.youtube.com/playlist?list=PL-wATfeyAMNqIee7cH3q1bh4QJFAaeNv0) diff --git a/discrete_fourier_transform.md b/discrete_fourier_transform.md new file mode 100644 index 0000000..81b20ad --- /dev/null +++ b/discrete_fourier_transform.md @@ -0,0 +1,53 @@ +# Discrete Fourier transform + +Discrete Fourier transform (DFT) is a function $\mathcal{F}: \mathbb{C}^n +\rightarrow \mathbb{C}^n$: + +$$ +\mathcal{F}(x)_j = \sum_{k = 0}^{n -1} x_k \cdot \omega_n^{jk}, +\quad j \in \{0..n-1\}, +$$ + +where $\omega_n$ is the [n-th primitive root of 1](./primitive_root_of_one.md). + +## What it is + +DFT is essentially evaluating polynomial $(x_0, x_1, \ldots, x_{n-1})$ at points +$(\omega_n^{0}, \omega_n^{1}, \ldots, \omega_n^{n-1})$. The reason why we chose +$\omega_n^j$, is due to the characteristics of n-th primitive roots of one, +thanks to which we can evaluate DFT in $O(n\log n)$ time using algorithm called +[Fast Fourier Transform](./fft.md). + +## There is an inverse too + +Since DFT is a linear projection: +$$ +\mathcal{F}(\mathbf{x}) = \mathbf{\Omega} \mathbf{x}, \;\text{s.t.}\; +\mathbf{\Omega}_jk = \omega^{jk} +$$ + + +There is also an inverse: +$$ +\mathbf{\Omega^{-1}} = \frac{\overline{\mathbf{\Omega}}}{n} +$$ + + +## Why is it useful + +DFT is useful for many things. Here is some short list to give you an idea. + +### Multiplying polynomials + +For a naive multiplication of two polynomials of size $n$, we would need +quadratic time. Since polynomial of size $n$ is defined by arbitrary, but different +$n$ points. We can do the following: +1. Evaluate each polynomial in $n$ points using DFT -- $O(n\log n)$ +2. Multiply corresponding pair of coefficients together -- $O(n)$ +3. Do inverse of DFT -- $O(n\log n)$ + +### Spectral analysis of a signal + +For a signal (e.g. sound) we can use DFT to find pure frequencies the signal is +made up of. This is somehow more involved and there is a separate note dedicated +to [Spectral analysis of sound](./spectral_analysis_of_sound.md). diff --git a/fft.md b/fft.md new file mode 100644 index 0000000..1432a06 --- /dev/null +++ b/fft.md @@ -0,0 +1,129 @@ +# Fast Fourier transform + +Fast Fourier transform (FFT) computes [Discrete Fourier +transform (DFT)](./discrete_fourier_transform.md) in $O(n\log n)$ time. + +Naive evaluation would run in $O(n^2)$ time since we need to evaluate polynomial +$x_0, \ldots, x_{n-1}$ in $n$ points $(P(z_0), \ldots, P(z_{n-1}))$. + +## Idea of FFT + +The idea of FFT is pretty simple: + +- We assume: $n = 2^l$ for some $l$, if $n$ is smaller, we pad it with zeros. + +- divide the polynomial $P$ into even and odd powers: + +$$ +\begin{align} +P(z) &= +\left( + x_0 z^0 + + x_2 z^2 + + x_4 z^4 + + \ldots + + x_{n - 2} z^{n - 2} +\right) \\ +&\quad + +z \left( + x_1 z^0 + + x_3 z^2 + + x_5 z^4 + + \ldots + + x_{n - 1} z^{n - 2} +\right) \\ +&= P_{e}(z^2) + z P_o(z^2) +\end{align} +$$ + +- now we have to evaluate two polynomials that are half the original size at $n$ + points + +- Further, we strategically pick the $n$ points, s.t. they are paired: + +$$ +z_k = - z_j, \,\text{for some}\, i,j \in {0, \ldots, n-1} +$$ + +- now we only have to evaluate two polynomials that are half the original size + at half the points since $z_k^2 = z_j^2$: + +$$ +\begin{align} +P(z_k) &= P_e(z_k^2) + z_k P_o(z_k^2) \\ +P(z_j) &= P_e(z_k^2) - z_j P_o(z_k^2) \\ +\end{align} +$$ + +- We apply the same idea recursively, which gives us $\log_2 n$ iterations. The + number of $z$s to evaluate also decreases by a factor of 2 ($z$s are + paired), which means at level $l$ we are going to be computing $\frac{1}{2^l}$ + $z$s. However the number of branches also grows exponentially which mean + at each depth, we are going to be doing $n$ computations. This means $O(n\log + n)$ time in total. + +The only hiccup is that after the first iteration, the $z$s are all going +to be positive and we will not be able to find $k, j$ s.t. $z^2_j = - +z^2_k$. This is at least true for **real numbers**. + +For complex numbers there is a convenient option for $z_k$ to be the [n-th +root of 1](./primitive_root_of_one.md) at the power of $k$. This means that we +define $z$ as: + +$$ +z_k = \omega^k, +$$ + +where $\omega$ is n-th primitive root of one. Thanks to the characteristics of +primitive roots, the following are true: + +- $\omega^j \ne \omega^k$ for $0 \le j < k < n$ -- ergo we will get value of $P$ + at $n$ different points +- For even $n$: $\omega^{\frac{n}{2} + j} = \omega^\frac{n}{2} \cdot \omega^j = + -\omega^j$ -- ergo we can find $j, k$ such that $z_k$ and $z_j$ are paired +- For even $n$: $\omega^2$ is $\frac{n}{2}$-th primitive root of 1 + - all powers are unique and less then one $(\omega^2)^0 < (\omega^2)^1 < + \ldots < (\omega^2)^{\frac{n}{2} - 1} < (\omega^2)^\frac{n}{2} = 1 + - meaning we can find pairing on every depth as long as $n = 2^l$ + +### Summary + +The reason why the algorithm is as fast is thanks to the fact that we can +divide the problem to 2 parts, each of which is four times easier than the +original solution (half the polynomial size, half of the evaluation points). + +## Algorithm + +```python +def fft(x: list[float], n: int, w: complex) -> list[complex]: + """Computes fft. + + Parameters + ---------- + x + Vector of n numbers. + n + n = 2**l for some l + w + Primitive n-th root of 1 + + Returns + ------- + Vector of n complex Fourier coefficients. + """ + if n == 1: + return x[0] + + evens = fft(x[::2], n/2, w**2) + odds = fft(x[1::2], n/2, w**2) + + coefs = [0 for _ in range(n)] + w_to_i = 1 + for i in range(n/2): + coefs[i] = evens[i] + w_to_i * odds[i] + # w^{n/2 + i} = -w^i + coefs[n/2 + i] = evens[i] - w_to_i * odds[i] + w_to_i *= w + + return coefs +``` diff --git a/imgs/5_primitive_root_of_one.png b/imgs/5_primitive_root_of_one.png new file mode 100644 index 0000000000000000000000000000000000000000..302accabafd3049a47b20afe6d9491aef304b58f GIT binary patch literal 38322 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 literal 0 HcmV?d00001 diff --git a/primitive_root_of_one.md b/primitive_root_of_one.md new file mode 100644 index 0000000..d76cfea --- /dev/null +++ b/primitive_root_of_one.md @@ -0,0 +1,25 @@ +# Primitive root of 1 + +In complex numbers, 1 has many n-th roots, but not all of them are primitive. +Number $x \in \mathbb{C}$ is primitive n-th root of 1 if $x^n = 1$ and $x^0, +x^1, \ldots, x^{n-1} \ne 1$. + +Each complex number has at least 2 N-th primitive roots: $\omega = +e^{2{\pi}i/n}$ and $\overline{\omega} = e^{-2{\pi}i/n}$. These come from [Eulers +formula](./eulers_formula.md). If we imagine complex numbers on 2D plane, +computing their power is like rotating them counter-clockwise (for $\omega$) or +clockwise (for $\overline{\omega}$). We get N-th primitive root, by splitting +the circle in $n$ parts ($2\pi/n$) and taking the first one. + +![5-th primitive root of one. From Labyrint algoritmů +(https://pruvodce.ucw.cz/)](./imgs/5_primitive_root_of_one.png) + +## Characteristics + +N-th primitive roots have some nice properties: + +- $\omega^j \ne \omega^k$ for $0 \le j < k < n$ +- For even $n$: $\omega^\frac{n}{2} = \sqrt{\omega^n} = \sqrt{1} = -1$ (it + cannot be 1 since $\frac{n}{2} < n$ and $\omega$ is n-th primitive root of 1) +- For even $n$: $\omega^{\frac{n}{2} + j} = \omega^\frac{n}{2} \cdot \omega^j = + -\omega^j$ diff --git a/spectral_analysis_of_sound.md b/spectral_analysis_of_sound.md new file mode 100644 index 0000000..bc8263d --- /dev/null +++ b/spectral_analysis_of_sound.md @@ -0,0 +1,9 @@ +# Spectral analysis of sound signal + +Spectral analysis of sound signal describes how to split up any sound signal to +pure frequencies the sound is made up of. This is done using [Discrete Fourier +transform (DFT)](./discrete_fourier_transform.md). + +TODO: +- why we can do it -- what properties are necessary, some intuition for it +- how do we do it