A naive :math:`O(N^2)` implementation of the 1D discrete Fourier transform (DFT). Notes ----- The Fourier transform decomposes a signal into a linear combination of sinusoids (ie., basis elements in the space of continuous periodic functions). For a sequence :math:`\mathbf
(frame, positive_only=True)
| 222 | |
| 223 | |
| 224 | def DFT(frame, positive_only=True): |
| 225 | """ |
| 226 | A naive :math:`O(N^2)` implementation of the 1D discrete Fourier transform (DFT). |
| 227 | |
| 228 | Notes |
| 229 | ----- |
| 230 | The Fourier transform decomposes a signal into a linear combination of |
| 231 | sinusoids (ie., basis elements in the space of continuous periodic |
| 232 | functions). For a sequence :math:`\mathbf{x} = [x_1, \ldots, x_N]` of N |
| 233 | evenly spaced samples, the `k` th DFT coefficient is given by: |
| 234 | |
| 235 | .. math:: |
| 236 | |
| 237 | c_k = \sum_{n=0}^{N-1} x_n \exp(-2 \pi i k n / N) |
| 238 | |
| 239 | where `i` is the imaginary unit, `k` is an index ranging from `0, ..., N-1`, |
| 240 | and :math:`X_k` is the complex coefficient representing the phase |
| 241 | (imaginary part) and amplitude (real part) of the `k` th sinusoid in the |
| 242 | DFT spectrum. The frequency of the `k` th sinusoid is :math:`(k 2 \pi / N)` |
| 243 | radians per sample. |
| 244 | |
| 245 | When applied to a real-valued input, the negative frequency terms are the |
| 246 | complex conjugates of the positive-frequency terms and the overall spectrum |
| 247 | is symmetric (excluding the first index, which contains the zero-frequency |
| 248 | / intercept term). |
| 249 | |
| 250 | Parameters |
| 251 | ---------- |
| 252 | frame : :py:class:`ndarray <numpy.ndarray>` of shape `(N,)` |
| 253 | A signal frame consisting of N samples |
| 254 | positive_only : bool |
| 255 | Whether to only return the coefficients for the positive frequency |
| 256 | terms. Default is True. |
| 257 | |
| 258 | Returns |
| 259 | ------- |
| 260 | spectrum : :py:class:`ndarray <numpy.ndarray>` of shape `(N,)` or `(N // 2 + 1,)` if `real_only` |
| 261 | The coefficients of the frequency spectrum for `frame`, including |
| 262 | imaginary components. |
| 263 | """ |
| 264 | N = len(frame) # window length |
| 265 | |
| 266 | # F[i,j] = coefficient for basis vector i, timestep j (i.e., k * n) |
| 267 | F = np.arange(N).reshape(1, -1) * np.arange(N).reshape(-1, 1) |
| 268 | F = np.exp(F * (-1j * 2 * np.pi / N)) |
| 269 | |
| 270 | # vdot only operates on vectors (rather than ndarrays), so we have to |
| 271 | # loop over each basis vector in F explicitly |
| 272 | spectrum = np.array([np.vdot(f, frame) for f in F]) |
| 273 | return spectrum[: (N // 2) + 1] if positive_only else spectrum |
| 274 | |
| 275 | |
| 276 | def dft_bins(N, fs=44000, positive_only=True): |
no outgoing calls